Sie könnten eine Funktion nimmt eine beliebige Anzahl von Attributen zu einer Gruppe von und konstruieren schaffen die groupingBy
- Collector
in einer Schleife, jedesmal, wenn die vorherige Version von sich selbst als Kollektor downstream
vorbei.
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);
}
Beachten Sie, dass die Reihenfolge der grouper
Funktionen umgekehrt wird, so dass Sie sie in umgekehrter Reihenfolge durchlaufen müssen (oder sie innerhalb der Funktion umkehren, zum Beispiel Collections.reverse
oder Guava der mit Lists.reverse
, je nachdem, was Sie bevorzugen).
Object groupedData = collectMany(data, Person::getTown, Person::getCountry, Person::getName);
Oder so, eine Old-School for
Schleife mit dem Array in der Funktion rückgängig zu machen, das heißt, Sie müssen nicht die Barsche in umgekehrter Reihenfolge passieren (aber IMHO ist dies schwieriger zu verstehen):
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);
}
Beide Ansätze geben eine Map<?,Map<?,Map<?,T>>>
zurück, genau wie in Ihrem ursprünglichen Code. Abhängig davon, was Sie mit diesen Daten machen möchten, könnte es auch sinnvoll sein, eine Map<List<?>,T>
zu verwenden, wie von Tunaki vorgeschlagen.
"Manchmal möchte ich nur nach Namen und Stadt gruppieren, manchmal nach Attributen." - abhängig von Benutzereingaben oder was? – Smutje
Siehe auch http://StackOverflow.com/a/34772639/1587046 –