
"A Sequence of elements supporting sequential and parallel aggregate operations"
위는 Stream의 공식 문서 설명이다.
스트림은 설명대로, 데이터를 순차적 또는 병렬적으로 집계하여 처리할 수 있는 기능들을 제공하는 라이브러리이다. (병렬처리 -> spliterator 이용)
이전까지는 데이터를 집계하기 위해서 for문 기반의 집계 로직을 설계하였다. 시간이 흐름에 따라, 데이터의 복잡성이 높아지고 처리해야할 로직이 증가하면서 반복문 기반의 데이터 집계 방식은 가독성이 많이 떨어졌는데, 이러한 문제점을 해결해주기 위해 stream이 등장했다고 해석하면 될 것 같다.
데이터를 저장하기 위한 공간이 아니다.
(source로부터 온 데이터를 computational pipe로 넘기는 과정)
Functional in nature
stream을 통해 들어온 데이터가 수정되더라도, source의 데이터는 변경되지 않는다 (= Side effect가 없다)
Stream Operation은 Intermediate, Terminate 으로 구분된다.
Intermediate : 스트림을 반환하는 Operation 또 다른 Intermediate operation으로 체이닝이 가능하다.
Terminate : 스트림이 아닌 타입을 반환하는 Operation
최종 스트림에서 데이터를 집계하는 역할을 담당한다.
Intermediate Operation은 Lazy하게 동작한다.
성능상의 이점을 위해서, Stream의 결과는 Terminal Operation이 등장할 때 평가한다.
Possibly unbounded
컬렉션 타입과 달리, 스트림을 지나가는 데이터의 개수가 무제한이다. (Short-Circuit을 통해서 제한을 걸수도 있다)
Consumable
스트림으로 처리하는 데이터는 단 한번만 처리한다. 즉, 한번 지나갔던 데이터를 다시 참조할 수 없다 (단방향 Iterator와 유사하다)
Stream에서 여러 개의Intermediate Operation과 하나의 Terminate Operation으로 최종 결과를 받아올 때, Stream의 Operation 집합을 Stream pipeline이라고 한다.
Consumable하고 Intermediate Operation의 결과가 다른 Intermediate Operation의 input으로 들어온다는 사실에 매우 적절한 이름이라고 생각한다!
Stream API 중 가장 많이 쓰이는 몇 개만 선정하여 기술하겠다.
Collection.stream()중계형 오퍼레이션
Stream.map(Function<T, R> mapper)
List<String> names = new ArrayList<String>(Arrays.asList("a","b","c","D"));
names.stream()
.map(s -> s.toUpperCase())
.forEach(System.out::println);
Stream.filter(Predicate<T> predicate)true) 원소들만 Stream에 포함시킨다.
List<String> names = new ArrayList<String>(Arrays.asList("a","b","c","D"));
names.stream()
.filter(s -> {
for (char c : s.toCharArray()) {
if (Character.isUpperCase(c))
return false;
}
return true;
})
.forEach(System.out::println);
Stream.flatMap(Function<T, R> mapper)List<List<String>> Stream<List<String>>flatMap은 이런 중첩 구조를 flatten해준다.
map()으로 생성된 stream은 String[]의 형식을 가진다.flatMap을 호출하면 String[]의 각각의 값(String)에 대해서 stream을 만든 다음, 이것을 하나의 스트림 (Stream<String>)으로 만들어 주는 것을 확인해 볼 수 있다.List<String> names1 = new ArrayList<String>(Arrays.asList("a","b","c","D"));
List<String> names2 = new ArrayList<String>(Arrays.asList("A","B","C","d"));
List<List<String>> nameTable = new ArrayList<>();
nameTable.add(names1);
nameTable.add(names2);
List<String> collect = nameTable.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
종료형 오퍼레이션
집계 처리를 위한 종료형 오퍼레이션이다.
종료형 오퍼레이션을 수행할 때, 중계형 오퍼레이션에 대한 순차적으로 수행하고 종료형 오퍼레이션을 수행하면 집계된 결과를 얻을 수 있다.
collect(Collector<?>)Collectors 클래스에서 원하는 결과 컬렉션으로 변환해주는 정적 팩토리를 지원한다.Collectors.toList(), Collectors.toSet()forEach(Consumer<>)anyMatch(Predicate<>), allMatch(Predicate<>), noneMatch(Predicate<>)count()