•
분할은 분할 함수라 불리는 프레디케이트를 분류 함수로 사용하는 특수한 그룹화 기능이다.
•
분할 함수는 불리언을 반환하므로 맵의 키 형식은 Boolean이다.
•
결과적으로 그룹화 맵은 최대 두 개의 그룹으로 분류된다.
•
만약 채식만 골라내고 싶다면 다음과 같이 코드를 작성할 수 있다.
Map<Boolean, List<Dish>> partitionedMenu = menu.stream().collect(partitioningBy(Dish::isVegetarian));
Java
복사
◦
위 코드를 실행하면 다음과 같은 맵이 반환된다.
◦
{false = [pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}
◦
해당 맵의 true 키를 참조하면 모든 채식 요리를 얻을 수 있다.
1. 분할의 장점
•
분할 함수가 반환하는 참, 거짓 두 가지 요소의 스트림 리스트를 모두 유지한다는 것이 장점이다.
•
이전 예제에서 partitionedMenu 맵에 false 키를 이용해서 채식이 아닌 모든 요리도 가져올 수 있다.
◦
다음 코드에서 보여주는 것처럼 컬렉터를 두 번째 인수로 전달할 수 있는 오버로드된 버전의 partioningBy 메서드도 있다.
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType = menu.stream()
.collect(partitioningBy(Dish::isVegetarian,
groupingBy(Dish::getType)));
Java
복사
◦
이는 앞에서 다룬 두 수준 그룹화 결과와 비슷하다. 또한 이전 코드를 활용하면 채식 요리와 채식이 아닌 요리에서 각각 칼로리가 가장 높은 요리도 찾을 수 있다.
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType = menu.stream()
.collect(partitioningBy(Dish::isVegetarian,
collectingAndThen(maxBy(comparingInt(Dish::getCalories)), Optional::get);
Java
복사
◦
앞에서 말했듯 분할이란 특수한 종류의 그룹화이다. partitioningBy가 반환한 맵 구현은 참과 거짓 두 가지 Key만 포함하므로 더 간결하고 효과적이다.
◦
사실 내부적으로 partitioningBy는 특수한 맵과 두 개의 필드로 구현되었다.
◦
이 외에도 groupingBy와 partitioningBy 컬렉터의 비슷한 점이 또 있다. 바로 다수준으로 분할하는 기법이다.
2. 숫자를 소수와 비소수로 분할하기
•
정수 n을 인수로 받아서 2에서 n까지의 자연수를 소수와 비소수로 나누는 프로그램을 구현해보자.
•
먼저 주어진 수가 소수인지 아닌지 판단하는 프레디케이트가 필요할 것이다.
public boolean isPrime(int candidate) {
return IntStream.range(2, (int) Math.sqrt((double) candidate))
.noneMatch(i -> candidate % i == 0);
}
Java
복사
•
이제 스트림을 만든 다음, 구현한 isPrime 메서드를 프레디케이트로 이용하고 partitioningBy 컬렉터로 리듀스해서 숫자를 분류할 수 있다.
Instream.rangeClosed(2, candidate).boxed()
.collect(partioningBy(candidate -> isPrime(candidate)));
Java
복사