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());
}
Secondary sorting in MapReduce framework is a technique to sort the output of the reducer based on the values unlike the default one where output of the MapReduce framework is sorted based on the key of the mapper/reducer.
Secondary sorting means sorting to be done on two or more field values of the same or different data types. Additionally we might also have deal with grouping and partitioning. The best and most efficient way to do secondary sorting in Hadoop is by writing our own key class.
A new sort key can be created from two or more sort keys by lexicographical order. The first is then called the primary sort key, the second the secondary sort key, etc. For example, addresses could be sorted using the city as primary sort key, and the street as secondary sort key.
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.
Assuming list
's type is List<Map<String,Object>>
(it's not clear what's the type of the value of the Map
, so I used Object
), your Comparator
should be able to compare two Map<String,Object>
instances.
Collections.sort(list, new Comparator<Map<String,Object>>() {
public int compare(Map<String,Object> o1, Map<String,Object> o2) {
// first compare o1.get("count") to o2.get("count")
// if they are equal, compare o1.get("name") to o2.get("name")
// don't forget to handle nulls (for example if either o1 or o2 is null
// or if any of the keys are not present in one or both of the maps)
}
If I understand correctly, you have a List<Map<String, Object>>
. You'll need to write a custom Comparator
in order to sort it. There, you can compare each entry separately (error handling removed for bravity):
public class ListMapComparator implements Comparator<List<Map<String, Object>>> {
@Override
public in compare (List<Map<String, Object>> l1, List<Map<String, Object>> l2) {
Integer count1 = (Integer)l1.get("count");
Integer count2 = (Integer)l2.get("count");
int comp = count1.compare(count2);
if (comp != 0) {
return comp;
}
String name1 = (String)l1.get("name");
String name2 = (String)l2.get("name");
return name1.compare(name2);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With