Thomas Z. Thomas Z. - 2 months ago 9x
Java Question

Manually chain GroupBy collectors

I want to group a list of person's. A person have some attributes like name, country, town, zipcode, etc. I wrote the static code, which works very well:

Object groupedData =, Collectors.groupingBy(Person::getCountry, Collectors.groupingBy(Person::getTown))));

But the problem is, that is it not dynamic. Sometimes I want to just group by name and town, sometimes by a attributes. How can I do this? Non Java 8 solutions are welcome as well.


You could create a function taking an arbitrary number of attributes to group by and construct the groupingBy-Collector in a loop, each time passing the previous version of itself as the downstream collector.

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

Note that the order of the grouper functions is reversed, so you have to pass them in reversed order (or reverse them inside the function, e.g. using Collections.reverse or Guava's Lists.reverse, whichever you prefer).

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

Or like this, using an old-school for loop to reverse the array in the function, i.e. you don't have to pass the groupers in inverse order (but IMHO this is harder to comprehend):

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);

Both approaches will return a Map<?,Map<?,Map<?,T>>>, just as in your original code. Depending on what you want to do with that data it might also be worth considering using a Map<List<?>,T>, as suggested by Tunaki.