Search code examples
javasortingsecondary-sort

How can I do a secondary sorting on a list of maps


Lets suppose I have the following list of maps

[{id:1,count:2,name:xyz},
 {id:2,count:3,name:def},
 {id:3,count:2,name:abc},
 {id:4,count:5,name:ghj}
]

I first want to sort this map by count and then by name:

Desired Output :

[{id:3,count:2,name:abc},
 {id:1,count:2,name:xyz},
 {id:2,count:3,name:def},
 {id:4,count:5,name:ghj}
]

I tried the following to perform the first sorting,but unable to sort using name after sorting by count

Collections.sort(list, new Comparator() {
      public int compare(Object o1, Object o2) {
           return ((Comparable) ((Map.Entry) (o1)).getValue())
          .compareTo(((Map.Entry) (o2)).getValue());
      }

Solution

  • With Java 1.8, I would use the new Comparator methods (although the lack of Type inference makes it necessary to declare all types, reducing the lisibility):

        final Comparator<Map<String, Comparable<Object>>> nameThenCountComparator = Comparator.<Map<String, Comparable<Object>>, Comparable<Object>> comparing(
                m -> m.get("name")).thenComparing(Comparator.<Map<String, Comparable<Object>>, Comparable<Object>> comparing(
                m -> m.get("count")));
    

    With Java 1.7, I would probably use a chainedComparator (see Apache's ComparatorUtils or Guava's Ordering) and a custom MapValueComparator (there are probably one in common libraries, but haven't found it). Then the wanted ordering get quite readable :

        class MapValueComparator implements Comparator<Map<String, Object>> {
            private final String key;
    
            public MapValueComparator(final String key) {
                this.key = key;
            }
    
            @Override
            public int compare(final Map<String, Object> o1, final Map<String, Object> o2) {
                return ((Comparable<Object>)o1.get(key)).compareTo(o2.get(key));
            }
        }
    
        Comparator<Object> nameThenCountComparator = ComparatorUtils.chainedComparator(
                new MapValueComparator("name"), 
                new MapValueComparator("count")
        );
    

    And then use it (Java 7 or 8):

    final List<Map<String, Comparable<Object>>> list = null;
    Collections.sort(list, nameThenCountComparator);
    

    Rq: you should, as stated in other answers, check for nulls and absent keys in the MapValueComparator.