Stream

Drumj·2023년 2월 23일
0

오늘의 학습

오늘도 자바의 정석 기초편 : 챕터 14 람다와 스트림 을 공부하며 작성한다.
어제 람다를 공부하고 처음으로 유튜브 강의를 찾아서 들었다. 확실히 영상으로 직접 설명을 들으니까 더 이해가 잘되고 중간중간 수업과 연관된 다른 이야기도 해줘서 더 좋았던 것 같다. 우선 이 챕터 14를 강의와 함께 마무리 하고 추후에 복습할때 강의를 같이 들어볼까 한다. (시간이 많았으면 좋겠다....)


스트림

스트림은 다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것.

이전까지는 컬렉션 프레임워크를 사용해서 데이터를 다뤘다. List, Set, Map 등등이 있는데 이것들이 각각 사용방법이 달라서 완벽하게 표준화 되었다고 보긴 어려웠다는 것이다.

이것들을 다 스트림으로 만들어서 중간연산 -최종연산 - 결과 라는 순서를 거치게 만든다. 여기서 중간 연산은 n번 수행 할 수 있고 최종 연산은 딱 1번만 수행할 수 있다고 한다.

중간 연산 : 연산결과가 스트림인 연산. 반복적으로 적용가능(n번)
최종 연산 : 연산결과가 스트림이 아닌 연산. 단 한번만 적용가능(스트림의 요소를 소모)

stream.distinct().limit(5).sorted().forEach(System.out::println)
//     중복 제거  5개 제한  정렬(증간연산). 한개씩 출력(최종 연산)

스트림의 특징

  • 데이터 소스로부터 데이터를 읽기만할 뿐, 변경하지 않는다.
  • Iterator처럼 일회용이다. (필요하면 다시 스트림을 생성해야 함)
  • 최종 연산 전까지 중간 연산이 수행되지 않는다 - 지연 연산
  • 작업을 내부 반복으로 처리한다. forEach()
  • 작업을 병렬로 처리 - 병렬 스트림 parallel()
  • 기본형 스트림 -IntStream, LongStream, DoubleStream 숫자와 관련된 유용한 메서드를 Stream<T>보다 많이 제공한다.

--스트림 만들기--

1. 컬렉션

  • Collection 인터페이스의 stream()으로 컬렉션을 스트림으로 변환
Stream<E> stream() // Collection 인터페이스의 메서드

List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream();	// list를 스트림으로 변환

//스트림의 모든 요소 출력
intStream.forEach(System.out::println); //12345, 최종 연산
intStream.forEach(System.out::println); //에러. 이미 스트림이 닫혔다.

2. 배열

Stream과 Arrays에 static 메서드로 정의되어 있다.

Stream<T> Stream.of(T...values) //가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive)
// 마지막 코드는 일부만 만든다. (배열, 시작, 끝) 으로 되어 있고 끝은 포함되지 않는다

마지막 주석은 무엇을 의미하는 거지??? 바로 예제로 알아보자.

//객체 배열로 스트림 생성

Stream<String> strStream1 = Stream.of("a", "b", "c"); //abc
Stream<String> strStream2 = Stream.of(new String[]{"a","b","c"}); //abc
Stream<String> strStream3 = Arrays.stream(new String[]{"a","b","c"}); //abc
Stream<String> strStream4 = Arrays.stream(new String[]{"a","b","c"},1,3); //bc 시작과 끝 숫자를 바꾸면 변화

또 기본형 배열로도 만들 수 있다! 여기서는 stream 이 아닌 IntStream(또는 Long 이나 Doulble) 이라는 것에 주의 하자.

//기본형 배열 스트림
int[] intArr = {1, 2, 3, 4, 5};
IntStream intStream1 = Arrays.stream(intArr); //12345
IntStream intStream2 = Arrays.stream(intArr,0,3); //123
IntStream intStream3 = IntStream.of(1, 2, 3, 4, 5); //12345
IntStream intStream4 = IntStream.of(intArr); //12345

기본형 스트림이라는 것외에는 위의 코드와 똑같다!!
또 기본형 스트림은 count()외에도 sum(), average() 라는 메서드도 제공한다!

객체 스트림은 Stream<T> 에는 count()만 있고 sum(),average()는 없다..!!!


3. Random, 임의의 수

Random 클래스에는

IntStream ints()
LongStream longs()
DoubleStream doubles()

라는 인스턴스 메서드가 포함되어 있는데, 이 메서드들은 해당 타입의 난수들로 이루어진 스트림을 반환한다!

이 메서드들이 반환하는 스트림은 크기가 정해지지 않은 무한 스트림이다. limit()를 사용해서 유한 스트림으로 바꿔주자!

IntStream intStream = new Random().ints(); //무한 스트림
intStream.limit(5).forEach(System.out::println); //limit(5)로 개수 지정.

만약 이 limit()를 치기 귀찮으면!! ints(5)로 해도 된다! 코드를 보자

IntStream ints(long streamSize)
LongStream longs(long streamSize)
DoubleStream doubles(long streamSize)

IntStream intStream = new Random().ints(5); //크기가 5인 난수 스트림을 반환

이 난수들의 범위는 아래와 같다.

Integer.MIN_VALUE <= ints() <= Integer.MAX_VALUE
Long.MIN_VALUE <= longs() <= Long.MAX_VALUE
0.0 <= doubles() < 1.0

4. 특정 범위의 정수

이어서 바로 특정 범위를 알아보자!

우선 위에서 바로 이어지게 ints()들을 알아보고 range(), rangeClosed()에 대해 알아보자!

//1번 방법, ints()에 범위 주기. 
IntStream intStream = new Random().ints(3,9);
intStream.limit(5).forEach(System.out::println);
//결과는 3~8 사이의 숫자가 5개 나온다! limit()가 없으면 계속계속 나온다!

//2번 방법, ints()에 사이즈와 범위 주기.
IntStream intStream = new Random().ints(5,3,9);
intStream.forEach(System.out::println);
//여기는 limit가 없어도 됨! ints에서 (size, start, end) 가 정해진다!

해당 메서드를 자세히 알아보자!!

//1번 방법
IntStream ints(int begin, int end)
LongStream longs(long begin, long end)
DoubleStream doubles(double begin, double end)

//2번 방법
IntStream ints(long streamSize, int begin, int end)
LongStream longs(long streamSize, long begin, long end)
DoubleStream doubles(long streamSize, double begin, double end)

차근 차근 알아보니 ints()가 아주 여러개로 쓰인다. 헷갈리지 말자!

자 다음으로는 range(), rangeClosed()에 대해 알아보자

IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)

ints()와 뭐가 다른걸까??

range() : 끝(end)이 포함되지 않음.
rangeClosed() : 끝(end)이 포함 됨.

IntStream intStream = IntStream.range(1,5); // 1,2,3,4
IntStream intStream = IntStream.rangeClosed(1,5); //1,2,3,4,5

이게 다다...! 그리고 이 메서드들은 IntStreamLongStream에만 있다!!


5. 람다식 iterate(), generate()

람다식을 매개변수로 받아서, 이 람다식에 의해 계산되는 값들을 요소로 한는 무한 스트림을 생성

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) //이전 요소에 종속적
static <T> Stream<T> generate(Supplier<T> s) //이전 요소에 독립적

iterate()는 이전 요소를 seed로 해서 다음 요소를 계산한다.

Stream<Integer> evenStream = Stream.iterate(0, n -> n+2);
evenStream.limit(6).forEach(System.out::println);
//결과
0,2,4,6,8,10

generate()는 seed를 사용하지 않는다.

// 계속 난수를 생성하는 스트림
Stream<Double> randomStream = Stream.generate(Math::random);

//계속 1을 출력하는 스트림
Stream<Integer> oneStream = Stream.generate(() -> 1);

6. 파일과 빈 스트림

  • 파일을 소스로 하는 스트림 만들기
    java.nio.file.Files에 list()
Stream<Path> Files.list(Path dir) //Path는 파일 또는 디렉토리

Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)
Stream<String> lines() // BufferedReader 클래스의 메서드

해당 부분은 예제없이 넘어갔다. 아직은 잘 안쓰는 단계라서 그런가?

  • 비어있는 스트림 만들기
Stream emptyStream = Stream.empty(); // empty()는 빈 스트림을 생성해서 반환한다
long count = emptyStream.cout(); // count의 값은 0! 빈 스트림이니 당연하다

마무리

여기까지가 스트림이 무엇이고 어떻게 작성하고 어떻게 생성하는가! 까지이다

스트림을 사용할때는

  1. 스트림 생성
  2. 중간 연산
  3. 최종 연산

이렇게 3단계를 거친다고 한다.

이번에는 스트림의 생성까지만 알아봤다. 다음번에 중간 연산과 최종연산에 대해 알아보자!!

0개의 댓글