Skip to content

Grouping and Double sorting List

I went through all the manuals out there and all SO questions but still unable to figure this out…

I have a List (integer represents age):

List<Person> people = Arrays.asList
            new Person("bob", 10),
            new Person("sue", 4),
            new Person("tom", 37),
            new Person("jim", 10),
            new Person("boo", 4),
            new Person("ekk", 53),
            new Person("joe", 10)

I need to:

  • group the list by age,
  • sort by group sizes (descending),
  • sort by age (descending)

So using the example above the result would have to be like this:

{10=[bob, jim, joe],4=[sue, boo], 53=[ekk], 37=[tom]}

What I tried:

I tried with and without streams. I failed on both.

Note: I would lean toward no stream solution, because from my testing of the below code it seems like streams are much slower (I used System.nanotime()). These 3 operations will be done thousands of times each time, so it may make a slight difference.

Using streams here is what I did:

List<List<Person>> grpd = new ArrayList<>
                                    groupingBy(Person::getAge, toList())
grpd =, b) ->, a.size())).collect(toList());

No streams approach:

    Map<Integer, List<Person>> grouped = new HashMap<>();
    for (Person person : people)
        if (grouped.containsKey(person._age))
        } else
            List<Person> p = new ArrayList<>();
            grouped.put(person._age, p);

    List<Map.Entry<Integer, List<Person>>> entries = new ArrayList<>(grouped.entrySet());
    Collections.sort(entries, new Comparator<Map.Entry<Integer, List<Person>>>()
        public int compare(Map.Entry<Integer, List<Person>> o1, Map.Entry<Integer, List<Person>> o2)
            return, o1.getValue().size());
    Map<Integer, List<Person>> sortedBySize = new LinkedHashMap<>();
    for (Map.Entry<Integer, List<Person>> entry : entries)
        sortedBySize.put(entry.getKey(), entry.getValue());

Problem: I have no idea how to add the final sort on either case.

public class Person
    public String _name;
    public int _age;
    public int getAge() { return _age; }

    public Person(String name, int age)
        _name = name;
        _age = age;

    public String toString()
        return _name;


As you’ve also asked about a non-stream solution, here it is:

Map<Integer, List<Person>> grouped = new HashMap<>();
people.forEach(person -> grouped.computeIfAbsent(
        k -> new ArrayList<>())

This groups by age. Now let’s sort the entries, first by group size descending, then by age descending:

List<Map.Entry<Integer, List<Person>>> toSort = new ArrayList<>(grouped.entrySet());
    Comparator.comparingInt((Map.Entry<Integer, List<Person>> e) -> e.getValue().size())

Now, toSort is a sorted list of entries. You need to put those entries into a new map:

Map<Integer, List<Person>> sorted = new LinkedHashMap<>();
toSort.forEach(e -> sorted.put(e.getKey(), e.getValue()));

And sorted holds the result you want.