스트림(stream)은 Java 8
버전부터 람다와 함께 지원되기 시작한 기능이다. 이전까지 Java에서 데이터를 다룰 때 for
문과 Iterator
를 이용해왔다. 이러한 방식들은 코드 가독성과 재사용성이 떨어지고, 가장 큰 문제점은 데이터 소스마다 다른 방식으로 다뤄야 한다는 것이었다. Iterator 등을 이용해 표준화 했지만, Collections.sort()
, Arrays.sort()
등 같은 기능의 중복된 메서드들은 여전히 불편하다. 이를 해결하기 위해 stream
이 등장했다.
스트림은 데이터 소스를 추상화
하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의
해놓다. 아래 코드와 같이 스트림을 사용하면 코드가 간결해져 이해하기 쉽고, 재사용성 또한 높다.
String[] sArr = {"bbb", "ccc", "aaa"};
List<String> sList = Arrays.asList(sArr);
Stream<String> stream1 = Arrays.stream(sArr);
Stream<String> stream2 = sList.stream();
stream1.sorted.forEach(System.out::println);
stream2.sorted.forEach(System.out::println);
→ stream은 데이터 소스로부터 데이터를 읽기만할 뿐, 데이터 소스를 변경하지는 않음
List<String> sList = Arrays.asList({"bbb", "ccc", "aaa"});
Stream<String> stream = sList.stream();
// 정렬 결과를 새로운 List에 담아서 반환
List<String> sortedList = stream.sorted.collect(Collections.toList());
→ stream은 한번 사용하면 닫혀서 다시 사용할 수 없음
List<String> sList = Arrays.asList({"bbb", "ccc", "aaa"});
Stream<String> stream = sList.stream();
stream.sorted.forEach(System.out::println);
int numOfStr = stream.count(); // Error: 스트림이 이미 닫혔음
- 내부 반복: 반복문을 메서드 내부에 숨길 수 있음
- 스트림을 이용한 작업이 간결할 수 있는 비결
List<String> sList = Arrays.asList({"bbb", "ccc", "aaa"});
for(String s : sList)
System.out.println(s);
stream.forEach(System.out::println);
stream.distinct().limit(5).sorted().forEach(System.out::println);
중간 연산
- 연산 결과가 스트림인 연산
- 스트림에 연속해서 중간 연산할 수 있음
- 위 예제의 distinct(), limit(), sorted()
최종 연산
- 연산 결과가 스트림이 아닌 연산
- 스트림의 요소를 소모하므로 단 한번만 가능
- forEach()
중간연산 | 설명 |
---|---|
Stream<T> distinct() | 중복 제거 |
Stream<T> filter(Predicate<T> predicate) | 조건에 안 맞는 요소 제외 |
Stream<T> limit(long maxSize) | 스트림의 일부를 잘라냄 |
Stream<T> skip(Long n) | 스트림의 일부를 건너뜀 |
Stream<T> peek(Consumer<T> action) | 스트림의 요소에 작업 수행 |
Stream<T> sorted() Stream<T> sorted(Comparator<T> comparator) |
스트림의 요소를 정렬 |
Stream<R> map (Function<T, R> mapper)DoubleStream mapToDouble (ToDoubleFunction<T> mapper)IntStream mapToInt (ToIntFunction<T> mapper)LongStream mapToLong (ToLongFunction<T> mapper) |
스트림의 요소를 반환 |
Stream<R> flatMap (Function<T, Stream<R>> mapper)DoubleStream flatMapToDouble (Function<T, DoubleStream> mapper)IntStream flatMapToInt (Function<T, IntStream> mapper)LongStream flatMapToLong (Function<T, LongStream> mapper) |
스트림의 요소를 반환 |
최종연산 | 설명 |
---|---|
void forEach(Consumer<? Super T> action) void forEachOrdered(Consumer<? Super T> action) |
각 요소에 지정된 작업 수행 |
long count() | 스트림의 요소의 개수 반환 |
Optional<T> max(Comparator<? super T> comparator) Optional<T> min(Comparator<? super T> comparator) |
스트림의 최대값/최소값 반환 |
Optional<T> findAny() → 아무거나 하나 Optional<T> findFirst() → 첫 번째 요소 |
스트림의 요소 하나를 반환 |
boolean allMatch(Predicate<T> p) → 모두 만족 boolean anyMatch(Predicate<T> p) → 하나라도 만족 boolean noneMatch(Predicate<> p) → 모두 만족 X |
주어진 조건을 모든 요소가 만족시키는지, 만족시키지 않는지 확인 |
Object[ ] toArray() A[ ] toArray(IntFunction<A[ ]> generator) |
스트림의 모든 요소를 배열로 반환 |
Optional<T> reduce (BinaryOperator<T> accumulator)T reduce (T identity, BinaryOperator<T> accumulator)U reduce (U identity, BiFunction<U, T, U> accumulator, BinaryOperator<U> combiner) |
스트림의 요소를 하나씩 줄여가면서 계산 |
R collect (Collector<T, A, R> collector)R collect (Supplier<R> supplier, BiConsumer<R, T> accumulator, BiConsumer<R, R> combiner) |
스트림의 요소 수집 주로 요소를 그룹화하거나 분할한 결과를 컬렉션에 담아 반환하는 데에 사용 |
최종연산
이 수행되기 전까지중간연산
이 수행되지 않음- 최종연산이 수행돼야 스트림의 요소들이 중간연산을 거쳐 최종연산에서 소모됨
- 요소의 타입이 T인 스트림 →
Stream<T>
- Autoboxing과 Unboxing으로 인한 비효율을 줄이기 위해 기본형으로 다루는 스트림 제공
- IntStream, DoubleStream, LongStream
- 더 효율적
- 스트림의 장점 중 하나 →
병렬 처리
가 쉽다 - 내부적으로 프레임워크를 이용해 자동으로 연산을 병렬 수행
- 그저 스트림에
parallerl()
메서드를 호출하면 됨