"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()