Home > Back-end >  How to sort a map based on value and key to a list of Entry
How to sort a map based on value and key to a list of Entry

Time:03-23

How to sort a map based on value and key to a list of Entry

First sorted by Value desc, and then if Values are colliding sort by Key Desc

example in a given Map :

Map<String,Integer> data = new HashMap();
data.put("a",10);
data.put("b",3);
data.put("c",10);

Expected Order:

["c", 10], ["a",10], ["b",3]

CodePudding user response:

Write a Comparator<Map.Entry<String,Integer>> that compares map entries according to your rule. Get a list of entries with new ArrayList<>(map.entrySet()) out of the map. Sort that list with Collections.sort(list, comparator).

CodePudding user response:

Use stream api and custom comparer.

    static int comparer(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
        //* 1000 here just to make sure value has higher priority
        // maybe *3 is already enough?
        // just want to avoid if else here
        return e2.getValue().compareTo(e1.getValue()) * 1000
                  e2.getKey().compareTo(e1.getKey());
    }

    public static void main(String args[]){
        Map<String, Integer> data = new HashMap();
        data.put("a", 10);
        data.put("b", 3);
        data.put("c", 10);
        data.entrySet().stream()
                .sorted((o1, o2) -> comparer(o1, o2))
                .forEach((kv -> System.out.println(
                        String.format("%s\t%s", kv.getKey(), kv.getValue()))));
//            c 10
//            a 10
//            b 3
        }
    }

CodePudding user response:

For that you need to define a Comparator.

You can utilize for that purpose static methods that were added as an enhancement of Java 8 to the Comparator and Map.Entry interfaces.

Both methods Map.Entry.comparingByValue() and Map.Entry.comparingByKey() will generate a comparator for values and keys respectively. In order to obtain a descending order reversed() method needs to be applied on them.

Both comparators are chained together with the thenComparing() method.

Comparator<Map.Entry<String, Integer>> valDescThenKeyDesc =
       Map.Entry.<String, Integer>comparingByValue().reversed()
           .thenComparing(Map.Entry.<String, Integer>comparingByKey().reversed());

Note that compiler unable to infer the correct types based on resulting type of the comparator and both comparingByValue() and comparingByKey()need to be provided with generic type information explicitly <String, Integer> (types of keys and values respectively).

for more information on how to build comparators with Java 8 methods take a look at this tutorial

And that how you can apply this comparator by using Stream API.

Create a stream of entries. Apply sorting() operation by passing the given comparator and collect the result to list by applying the terminal operation collect().

public static List<Map.Entry<String, Integer>> getMapEntryList(Map<String,Integer> data,
                                                               Comparator<Map.Entry<String, Integer>> comparator) {
    return data.entrySet().stream()
            .sorted(comparator)
            .collect(Collectors.toList());
}

main()

public static void main(String[] args) {
    Map<String,Integer> data = Map.of("a",10, "b",3,"c",10);

    Comparator<Map.Entry<String, Integer>> valDescThenKeyDesc =
            Map.Entry.<String, Integer>comparingByValue().reversed()
                    .thenComparing(Map.Entry.<String, Integer>comparingByKey().reversed());

    List<Map.Entry<String, Integer>> result = getMapEntryList(data, valDescThenKeyDesc);
    
    System.out.println(result);
}

Output

[c=10, a=10, b=3]

CodePudding user response:

What you're looking for is called a "stable" sorting algorithm. What that means is that when sorting, identical objects stay in the order they've been in to begin with.
Example Stable:

{ ['a', 5], ['b', 5], ['a', 10] } -> { ['a', 5], ['a', 10], ['b', 5] }

Example not Stable:

{ ['a', 5], ['b', 5], ['a', 10] } -> { ['a', 10], ['a', 5], ['b', 5] }

One thing to note is that you'll need to sort by keys first to get them in order, then by values to get the actual sort you want.

CodePudding user response:

public class Person implements Comparable<Person>{

    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person person) {
        if(this.age!= person.age){
            return person.age.compareTo(this.age);
        } else {
         return person.name.compareTo(this.name);
        }
    }

    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '\''  
                ", age="   age  
                '}';
    }

    public static void main(String argsp[]){
        Map<String,Integer> data = new HashMap();
        data.put("a",10);
        data.put("b",3);
        data.put("c",10);

        List<Person> collect = data.entrySet().stream().map(entry -> new Person(entry.getKey(), entry.getValue())).collect(Collectors.toList());
        Collections.sort(collect);
        collect.stream().forEach(person-> System.out.println(person));

    Collection<Map.Entry<String,Integer>> result  = collect.stream().map(person -> 
            new AbstractMap.SimpleEntry<String, Integer>(person.name, person.age)).collect(Collectors.toList());


    }
}

results:

Person{name='c', age=10}
Person{name='a', age=10}
Person{name='b', age=3}
  • Related