Home > Mobile >  How get a List of Entries sorted by Value and then by Key from a Map
How get a List of Entries sorted by Value and then by Key from a Map

Time:03-23

My question is how to sort a Map contents based on its values and keys and get a List of entries as a result?

First, entries need to be sorted by value in descending order, and then if values are colliding, sort them by key in descending order as well.

Example of the 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:

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 comparingByValue() and comparingByKey() of the Map.Entry interface 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 is unable to infer the correct types of the arguments of comparingByValue() and comparingByKey() based only on the resulting type of the comparator.

Therefore, both comparingByValue() and comparingByKey()need to be provided with the 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

The next step is to create a sorted list of entries.

For that, you can either create a List of entries manually by passing an entry set to the constructor and then apply the method sort() on it, or by making use of the Stream API

In order to implement it with streams, first, we need to obtain a stream of entries. Apply sorting() operation by passing the given comparator and collect the result into a 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:

Use stream api and custom comparer.

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {

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

  public static void main(String[] args) {
    var sorted = createTestMap().entrySet().stream()
        //* 1000 here just to make sure value has higher priority
        // maybe *3 is already enough?
        // just want to avoid if else here
        .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()) * 1000
              e2.getKey().compareTo(e1.getKey()))
        .collect(Collectors.toList());
    for (var kv : sorted) {
      System.out.println(
          String.format("%s\t%s", kv.getKey(), kv.getValue()));
    }
  }
}

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:

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