17.1 스트림이란?

지금까지 컬렉션 및 배열에 저장된 요소를 반복 처리하기 위해서는 for 문을 이용하거나 Iiterator(반복자)를 이용했음. 다음은 List 컬렉션에서 요소를 하나씩 처리하는 for 문임.

List<String> list = ...;
for(int i=0; i<list.size(); i++) {
	String item = list.get(i);
	//item 처리
}

그리고 Set에서 요소를 하나씩 처리하기 위해 Iiterator를 다음과 같이 사용함ㅁ.

Set<String> set = ...;
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
	String item = iterator.next();
	//요소 처리
}

Java 8부터는 또 다른 방법으로 컬렉션 및 배열의 요소를 반복 처리하기 위해 스트림을 사용할 수 있음. 스트림은 요소들이 하나씩 흘러가면서 처리된다는 의미를 가지고 있음. List 컬렉션에서 요소를 반복 처리하기 위해 스트림을 사용하면 다음과 같음.

Stream<String> stream = list.stream();
stream.forEach( item -> item 처리 );

List 컬렉션의 stream() 메소드로 Stream 객체를 얻고, forEach() 메소드로 요소를 어떻게 처리할지를 람다식으로 제공함. 다음 예제는 Set 컬렉션의 요소를 하나씩 읽고 출력하기 위해 스트림을 사용함.

package ch17.sec01.exam01;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        //Set 컬렉션 생성
        Set<String> set = new HashSet<>();
        set.add("홍길동");
        set.add("신용권");
        set.add("감자바");

        //Stream을 이용한 요소 반복 처리
        Stream<String> stream = set.stream();
        stream.forEach(name -> System.out.println(name));
    }
}

Stream은 Iterator와 비슷한 반복자이지만, 다음과 같은 차이점을 가지고 있음.

1) 내부 반복자이므로 처리 속도가 빠르고 병렬 처리에 효율적임.
2) 람다식으로 다양한 요소 처리를 정의할 수 있음.
3) 중간 처리와 최종 처리를 수행하도록 파이프 라인을 형성할 수 있음.

17.2 내부 반복자

for 문과 Iterator는 컬렉션 요소를 컬렉션 바깥쪽으로 반복해서 가져와 처리하는데, 이것을 외부 반복자라고 함. 반면 스틑림 요소 처리 방법을 컬렉션 내부로 주입시켜서 요소를 반복 처리하는데, 이것을 내부 반복자라고 함.

image.png

외부 반복자일 경우는 컬렉션의 요소를 외부로 가져오는 코드와 처리하는 코드를 모두 개발자 코드가 가지고 있어야 함. 반면 내부 반복자일 경우는 개발자 코드에서 제공한 데이터 처리 코드(람다식)를 가지고 컬렉션 내부에서 요소를 반복 처리함.

내부 반복자는 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있음. 하나씩 처리하는 순차적 외부 반복자보다는 효율적으로 요소를 반복시킬 수 있는 장점이 있음.

image.png

다음 예제는 List 컬렉션의 내부 반복자를 이용해서 병렬 처리하는 방법을 보여줌. parallelStream() 메소드로 병렬 처리 스트림을 얻고, forEach() 메소드를 호출할 때 요소 처리 방법인 람다식을 제공함. 람다식은 처리되는 요소가 무엇이고, 어떤 스레드가 처리하는지를 출력함.

package ch17.sec02;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class ParallelStreamExample {
    public static void main(String[] args) {
        //List 컬렉션 생성
        List<String> list = new ArrayList<>();
        list.add("홍길동");
        list.add("신용권");
        list.add("감자바");
        list.add("람다식");
        list.add("박병렬");

        //병렬 처리
        Stream<String> parallelStream = list.parallelStream();
        parallelStream.forEach(name -> {
            System.out.println(name + ": " + Thread.currentThread().getName());
        });
    }
}

17.3 중간 처리와 최종 처리