/////
Search
Duplicate
4️⃣

분할

분할은 분할 함수라 불리는 프레디케이트를 분류 함수로 사용하는 특수한 그룹화 기능이다.
분할 함수는 불리언을 반환하므로 맵의 키 형식은 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는 특수한 맵과 두 개의 필드로 구현되었다.
이 외에도 groupingBypartitioningBy 컬렉터의 비슷한 점이 또 있다. 바로 다수준으로 분할하는 기법이다.

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
복사