스트림의 요소를 선택하는 방법, 즉 프레디케이트 필터링 방법과 고유 요소만 필터링하는 방법을 배운다.
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
distinct
메서드도 지원한다.List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
List<Dish> filteredMenu = specialMenu.stream()
.filter(dish -> dish.getCalories() < 320)
.collect(toList());
<320칼로리보다 작은 요소 탐색>
List<Dish> slicedMenu1 = specialMenu.stream()
.takeWhile(dish -> dish.getCalories() < 320)
.collect(toList());
dropWhile
은 takeWhile
과 정반대의 작업을 수행한다.dropWhile
은 프레디케이트가 처음으로 거짓이 되는 지점까지 발견된 요소를 버린다.<320칼로리보다 큰 요소 탐색>
List<Dish> slicedMenu2 = sepcialMenu.stream()
.dropWhile(dish -> dish.getCalories() < 320)
.collect(toList());
List<Dish> dishes = specialMenu.stream()
.filter(dish -> dish.getCalories() > 300)
.limit(3)
.collect(toList());
skip(n)
메서드 지원map
, flatMap
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
getName
은 문자열을 반환 ➜ .map(Dish::getName)
의 출력 스트림은 Stream<String>
List<String>
<문제 발생>
words.stream()
.map(word -> word.split("")) // Stream<String[]>
.map(Arrays::stream) // Stream<Stream<String>>
.distinct()
.collect(toList()); // List<Stream<String>> -> 문제 발생
words.stream()
.map(word -> word.split("")) // Stream<String[]>
.flatMap(Arrays::stream) // Stream<String>
.distinct()
.collect(toList()); // List<String>
flatMap
bolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
쇼트서킷 기법
anyMatch, allMatch, noneMatch 세 메서드는 스트림 쇼트서킷 기법이다.
전체 스트림을 처리하지 않았더라고 결과를 반환할 수 있는 것을 의미한다.
Optional<T> findAny()
Optional<T> findFirst()
int sum = 0
for (int x : numbers) {
sum += x;
}
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
reduce
는 두 개의 인수를 갖는다.BinaryOperator<T>
int sum = numbers.stream().reduce(0, Integer::sum);
Optional<Integer> sum = nubmers.stream().reduce((a, b) -> (a + b));
Optional<Integer>
을 반환?Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> min = numbers.stream().reduce(Integer::min);
Comparator
를 인수로 받는 min
과 max
메서드를 제공한다.Optional<Transaction> smallestTransaction = transaction.stream()
.min(comparing(Transaction::getValue));
💡 reduce 메서드의 장점과 병렬화
- reduce를 이용하면 내부 반복이 추상화되면서 내부 구현에서 병렬로 reduce를 실행할 수 있게 된다.
- 반복적인 합계에서는 sum 변수를 공유해야 하므로 쉽게 병렬화하기 어렵다.
💡 스트림 연산 : 상태 없음과 상태 있음
map
,filter
등은 입력 스트림에서 각 요소를 받아 0 또는 결과를 출력 스트림으로 보낸다. 따라서 이들은 보통 상태가 없는, 즉 내부 상태를 갖지 않는 (stateless operation)이다.- 하지만
reduce
,sum
,max
연산은 결과를 누적할 내부 상태가 필요하다. 예제의 내부 상태는 작은 값이다. 스트림에서 처리하는 요소 수와 관계없이 내부 상태의 크기는 한정 (bounded)되어 있다.- 반면
sorted
나distinct
같은 연산은 스트림의 요소를 정렬하거나 중복을 제거하기 위해 과거의 이력을 알고 있어야 한다. 예를 들어 어떤 요소를 출력 스트림으로 추가하려면 모든 요소가 버퍼에 추가되어 있어야 한다. 연산을 수행하는 데 필요한 저장소 크기는 정해져있지 않다. 따라서 데이터 스트림의 크기가 크거나 무한이라면 문제가 생길 수 있다. 이러한 상태를 내부 상태를 갖는 연산(stateful operation)이라 한다.
int calories = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
➜ 스트림 API 숫자 스트림을 효율적으로 처리할 수 있도록 기본 특화 스트림을 제공한다.
IntStream
, DoubleStream
, LongStream
세 가지 기본형 특화 스트림을 제공한다.sum
, 최댓값 요소를 검색하는 max
등을 제공한다.mapToInt
, mapToDouble
, mapToLong
세가지 메서드를 가장 많이 사용한다.int calories = menu.stream()
.mapToInt(Dish::getCalories) // Stream<Dish> 반환
.sum(); // IntStream() 반환
IntStream intStream = menu.stream().mapToInt(Dish::getCalories); // 스트림 -> 숫자스트림
Stream<Integer> stream = intStream.boxed(); // 숫자스트림 -> 스트림
OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
프로그램에서는 특정 범위의 숫자를 이용해야 하는 상황이 자주 발생한다.
IntStream
과 LongStream
에서는 range
와 rangeClosed
라는 두 가지 정적 메서드를 제공한다.IntStream evenNumbers = IntStream.rangeClosed(1, 100) // [1, 100]의 범위를 나타냄
.filter(n -> n % 2 == 0); // 1 ~ 100까지의 짝수 스트림
IntStream.range(1, 100)
은 1과 100 포함하지 않는다.Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
Stream<String> emptyStream = Stream.empty();
Stream<String> homeValueStream = Stream.ofNullable(System.getProperty("home"));
System.getProperty
는 제공된 키에 대응하는 속성이 없으면 null
을 반환한다.int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
Arrays.stream
을 이용하여 기본형 int로 이루어진 배열을 IntStream
으로 변환할 수 있다.생략,,,
iterate
와 generate
에서 만든 스트림은 요청할 때마다 주어진 함수를 이용해서 값을 만든다.limit(n)
함수를 함께 연결해서 사용한다.public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
iterate
메서드는 초깃값과 람다를 인수로 받아서 새로운 값을 끊임없이 생산할 수 있다.public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
iterate
와 달리 generate
는 생산된 각 값을 연속적으로 계산하지 않는다.