스트림이란?

스트림 생성

스트림(Stream)은 자바 8부터 추가된 기능으로, 데이터 처리에 있어서 간결하고 효율적인 코드 작성을 가능하게 해준다

스트림을 이용하면 컬렉션(List, Set 등)이나 배열에 저장된 요소들을 반복문 없이도 간단하게 필터링(filter), 변환(map), 정렬(sorted) 등의 작업을 적용할 수 있다.

특히 스트림은 중간 연산과 최종 연산을 구분하며, 지연 연산(lazy evaluation)을 통해 불필요한 연산을 최소화한다.

스트림은 일반적으로 한 번 사용하면 재사용할 수 없다(소진되면 끝). 따라서, stream() 으로 얻은 스트림을 여러 번 순회하려면, 다시 스트림을 생성해야 한다.

image.png

public class CreateStreamMain {

    public static void main(String[] args) {
        System.out.println("1. 컬렉션으로부터 생성");
        List<String> list = List.of("a", "b", "c");
        Stream<String> stream1 = list.stream();
        stream1.forEach(System.out::println);

        System.out.println("2. 배열로부터 생성");
        String[] arr = {"a", "b", "c"};
        Stream<String> stream2 = Arrays.stream(arr);
        stream2.forEach(System.out::println);

        System.out.println("3. Stream.of() 사용");
        Stream<String> stream3 = Stream.of("a", "b", "c");
        stream3.forEach(System.out::println);

        System.out.println("4. 무한 스트림 생성 - iterate()");
        // iterate: 초기값과 다음 값을 만드는 함수를 지정
        Stream<Integer> infiniteStream = **Stream.iterate(0, n -> n + 2);**
        infiniteStream.limit(3).forEach(System.out::println);

        System.out.println("5. 무한 스트림 생성 - generate()");
        // generate: Supplier를 사용하여 무한하게 생성
        Stream<Double> randomStream = **Stream.generate(Math::random);**
        randomStream.limit(3).forEach(System.out::println);
    }
}

중간 연산

결과가 즉시 생성되지 않고, 최종 연산이 호출될 때 한꺼번에 처리된다는 특징이 있다(지연 연산).

image.png

FlatMap

map 은 각 요소를 하나의 값으로 변환하지만, flatMap 은 각 요소를 스트림(또는 여러 요소)으로 변환한 뒤, 그 결과를 하나의 스트림으로 평탄화(flatten)해준다.

[
 [1, 2],
 [3, 4], -> [1, 2, 3, 4, 5, 6]
 [5, 6]
]

// map -> 결과적으로 List<List<Integer>> List<Stream<Integer>> 가 되었다. 이것은 우리가 기대한 결과가 아
니다
List<Stream<Integer>> mapResult = outerList.stream()
        .map(list -> list.stream())
        .toList();
System.out.println("mapResult = " + mapResult);

// flatMap
// flatMap 을 쓰면 내부의 Stream 들을 하나로 합쳐 List<Integer> 를 얻을 수 있다.
// flatMap 은 중첩 구조(컬렉션 안의 컬렉션, 배열 안의 배열 등)를 일차원으로 펼치는 데 사용된다
List<Integer> flatMapResult = outerList.stream()
        .flatMap(list -> list.stream())
        .toList();
System.out.println("flatMapResult = " + flatMapResult);

최종 연산

최종 연산(Terminal Operation)은 스트림 파이프라인의 끝에 호출되어 실제 연산을 수행하고 결과를 만들어낸다. 최종 연산이 실행된 후에 스트림은 소모되어 더 이상 사용할 수 없다.

image.png