2016-08-01 4 views
17

사람의 목록을 그룹화하려고합니다. 즉, 동적 아니라,GroupBy 수집기를 수동으로 연결

Object groupedData = data.stream().collect(groupingBy(Person::getName, Collectors.groupingBy(Person::getCountry, Collectors.groupingBy(Person::getTown)))); 

그러나 문제는 : 사람은 내가 아주 잘 작동 정적 코드를 썼다 등 이름, 국가, 도시, 우편 번호, 같은 몇 가지 속성이 있습니다. 때로는 이름과 마을별로, 때로는 속성별로 그룹화하기를 원합니다. 어떻게해야합니까? 비 Java 8 솔루션도 환영합니다.

+0

"때로는 이름과 마을별로 가끔 속성으로 묶고 싶습니다." - 사용자 입력 또는 무엇에 따라? – Smutje

+1

참고 http://stackoverflow.com/a/34772639/1587046 –

답변

11

그룹화 할 임의의 수의 속성을 사용하여 함수를 생성하여 groupingBy - Collector을 반복하여 각각의 이전 버전을 downstream 콜렉터로 전달할 수 있습니다. grouper 기능의 순서는 반대 순서로 통과해야하므로 (또는 함수 내에서 그 역 예를 들어, Collections.reverse를 사용하거나 구아바의 Lists.reverse, 당신이 선호하는 중), 반전 것을

public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) { 
    Iterator<Function<T, ?>> iter = Arrays.asList(groupers).iterator(); 
    Collector collector = Collectors.groupingBy(iter.next()); 
    while (iter.hasNext()) { 
     collector = Collectors.groupingBy(iter.next(), collector); 
    } 
    return (Map) data.stream().collect(collector); 
} 

참고. 기능의 배열을 반대로 오래된 학교에 for 루프를 사용하여

Object groupedData = collectMany(data, Person::getTown, Person::getCountry, Person::getName); 

또는 같은

는, 당신은 반대 순서로 능성어를 통과하지 않아도 즉, (그러나 이럴이 이해하기 어렵습니다) :

public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) { 
    Collector collector = Collectors.groupingBy(groupers[groupers.length-1]); 
    for (int i = groupers.length - 2; i >= 0; i--) { 
     collector = Collectors.groupingBy(groupers[i], collector); 
    } 
    return (Map) data.stream().collect(collector); 
} 

두 가지 방법 모두 원래 코드와 마찬가지로 Map<?,Map<?,Map<?,T>>>을 반환합니다. 해당 데이터로 수행하려는 작업에 따라 Tunaki과 같이 Map<List<?>,T>을 사용하는 것이 좋습니다.

+0

저장된 내 하루! 멋지고 부드럽게 작동합니다. 감사! –

+0

왜 iterator 상용구를 모두 사용합니까? 배열을 통해 간단히 할 수 있습니다. – Clashsoft

+1

@Clashsoft 문제는 ​​첫 번째 항목을 다르게 취급해야한다는 것입니다. 내 첫 번째 버전은'foreach'와 함께 있었지만 첫 번째 항목을 처리하려면 세 가지가 필요했습니다. –

8

그룹화 할 속성으로 구성된 목록으로 그룹화 할 수 있습니다.

이름과 국가별로 그룹화하려는 경우를 생각해보십시오. 그럼 당신은 사용할 수 있습니다 : 같은 순서로 같은 요소를 포함 할 때 두 개의 목록이 동일한 것으로 간주되기 때문에

Map<List<Object>, List<Person>> groupedData = 
    data.stream().collect(groupingBy(p -> Arrays.asList(p.getName(), p.getCountry()))); 

이 작동합니다. 그러므로 결과 맵에서 각기 다른 이름/국가 쌍에 대한 키를 갖게되고 값으로 그 특정 이름 및 국가를 가진 사람 목록을 갖게됩니다. 다른 말로하면 "이름별로 그룹화하고 국가별로 그룹화"하는 대신 효과적으로 "이름과 국가별로 그룹화"라고 말합니다. 이점은지도 등의지도지도로 끝나지 않는 것입니다.

+0

"장점은지도의지도지도로 끝나지 않는다는 것입니다."그것이 OP가 실제로 끝내는 것이 아니라고 가정하면 ... –

+2

글쎄 그 요구 사항은 @tobias_k라는 질문의 일부가 아니 었습니다. 키, 값만있는지도는 알려지지 않은 내부지도 수준의지도로 작업하는 것이 더 쉽습니다. – Tunaki

+0

그것은 OP의 말에 따르면 OP의 코드가 현재하는 것인데, "매우 잘 작동합니다"라는 질문의 일부였습니다. 어쨌든, 나는 어떤 접근 방식이 당신이 데이터를 어떻게 사용 하느냐에 달려 있다고 생각합니다. 접근 방식이 더 쉬운 시나리오가 있습니다. –

관련 문제