업데이트:

Effective Java에서 배웠던 Stream을 정리한 포스트입니다.

Stream이란?


Java 8부터 지원하는 API로 컬렉션, 배열 등에 대해 요소 하나씩 반복적인 처리를 하도록 도와주는 기능. for문과 비슷하면서도 여러가지 다른 기능들이 있는데 하나 씩 차근차근 알아보도록 하겠습니다.

Stream의 특징


원본 데이터를 변경하지 않는다.

Stream은 원본 데이터를 변경하지 않습니다. 컬렉션, 배열 등의 요소를 읽어 특정 처리를 할 뿐 원본 컬렉션이나 배열을 변경하지 않습니다.

@Test
void Stream_테스트코드() {
    List<Integer> integers = List.of(1, 2, 3, 4, 5);
    Stream<Integer> intStream = integers.stream().map(i -> i + 1);

    System.out.println(">>>>>>>>>> integers");
    integers.stream().forEach(System.out::println);

    System.out.println(">>>>>>>>>> intStream");
    intStream.forEach(System.out::println);

}

not_change_stream_data

위의 코드를 보면 integers를 변환하는 map() 메소드를 사용하였지만 integers를 그대로 있고, 반환된 intStream만 변환되어 출력되는 것을 확인할 수 있습니다.

일회용이다.

이번에는 위의 코드에서 intStream을 다시 한번 사용해보도록 하겠습니다.

System.out.println(">>>>>>>>>> intStream1");
intStream.forEach(System.out::println);

System.out.println(">>>>>>>>>> intStream2");
intStream.forEach(System.out::println);

그러면 다음과 같은 에러를 확인할 수 있습니다.

re_user_stream_data_error

메시지에 적힌대로 stream은 이미 사용되었거나 닫혀 있다는 것을 확인할 수 있습니다.

즉, 재사용이 불가능 합니다.

Lazy 하다

먼저 Stream에는 중간 연산 메소드, 최종 연산 메소드가 있습니다.

중간 연산 메소드

  • 반환 타입이 Stream
  • filter(), map(), flatMap()

최종 연산 메소드

  • 반환 타입이 Stream이 아닌 다른 타입
  • findFirst(), allMatch(), collect(), count()

여기서 “Lazy 하다”라는 것은 마지막에 어떤 연산을 필요로 하는 것인지를 보고 연산을 시작한다는 의미로 받아들이시면 됩니다.

사바라다님의 블로그에서 좋은 예시가 있어 차용했습니다.

[java] Java와 Lazy evaluation - Java8 Stream

문제는 다음과 같습니다.

1부터 10까지 숫자 중 2의 배수 3개만 추출할 것.

먼저 forEach를 사용한 코드와 결과를 보겠습니다.

@Test
void lazy_테스트_for문() {
    int count = 0;
    List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    List<Integer> result = new ArrayList<>();
    for (int i : list) {
        System.out.println("i = " + i);
        if (i % 2 == 0 && count++ < 3) {
            result.add(i);
        }
    }

    System.out.println("result = " + Arrays.toString(result.toArray()));
}

lazy_for_result

결과는 의도한 대로 2, 4, 6를 잘 반환했고, list에 담긴 10개의 요소 모두 반복한 것으로 확인할 수 있습니다.

다음은 stream을 이용한 코드와 결과를 보겠습니다.

@Test
void lazy_테스트_stream() {
    List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    List<Integer> result = list.stream()
        .filter(i -> {
            System.out.println("i = " + i);
            return i % 2 == 0;
        })
        .limit(3)
        .collect(Collectors.toList());

    System.out.println("result = " + Arrays.toString(result.toArray()));
}

lazy_stream_result

결과는 위와 마찬가지로 2, 4, 6를 잘 반환했지만 다른 점이 있습니다. 바로 6까지만 반복했다는 사실인데요, Stream은 마지막에 limit(3)을 보고 3개의 결과가 나오면 더 이상 연산을 하지 않는다고 판단하고 6까지만 반복했다는 사실을 알 수 있습니다.

마무리


이번 포스트에선 Java 8에 추가된 Stream의 기본적인 내용에 대해 알아보았습니다. 다음은 자주 쓰이는 메소드에 대해 알아보도록 하겠습니다.

오늘도 제 포스트를 봐주셔서 감사합니다.😀

댓글남기기