6

How sort HashMap entries by multiple properties.

Suppose I have a map with key String and value as Object.

Map<String, UserMetrics> map = new HashMap<>
map.put("user10",new UserMetrics(1,100,111));
map.put("user3",new UserMetrics(10,330,444));
map.put("user11",new UserMetrics(333,100,555));
map.put("user1",new UserMetrics(1,111,433));

 public static class UsageMetrics implements Serializable {
        private long param1;
        private long param2;
        private long param3;....
 }

I want to sort users first by "param1" and then after by "param2"

result expected:<>

user10, UserMetrics(1,100,111)
user1,  UserMetrics(1,111,433))
user3,  UserMetrics(10,330,444));
user11, UserMetrics(333,100,555))
2
  • Try use Comparator.comparing and thenComparing function Commented Apr 12, 2018 at 8:58
  • @HadiJ Better comparingLong and thenComparingLong. Commented Apr 12, 2018 at 9:00

3 Answers 3

8

You can use the following comparator for sorting:

Comparator
        .comparingLong(UserMetrics::getParam1)
        .thenComparingLong(UserMetrics::getParam2);

The difficulty is, however that you want to sort values, not keys. It seems also you need both keys and values. For this you could make and sort a copy of the entry set of your map. Something along the lines:

List<Map.Entry<String, UserMetrics>> sortedEntries = new ArrayList<>(map.entrySet());
Collections.sort(sortedEntries,
    Map.Entry.comparingByValue(
        Comparator
            .comparingLong(UserMetrics::getParam1)
            .thenComparingLong(UserMetrics::getParam2)));

Alternatively you can also use a sorted collection (like TreeSet) or a sorted stream - normally you can provide your own comparator to the "sorting things".

Also note that I'm using comparingLong/thenComparingLong, unlike other answers where people just used comparing/thenComparing. The problem with comparing/thenComparing is that if you have primitive types like long, comparing/thenComparing will essentially box them into wrapper types like Long, which is totally unnecessary.

Sign up to request clarification or add additional context in comments.

4 Comments

This sorts by key. The OP wants to sort by value.
@HariMenon Ah, yes, my bad.
@HariMenon Corrected - let's remove our comments not to confuse people.
@lexicore , thanx :) !
2

You can use below code. I have written by using Employee the s value object, so you can use your own Object :

public class Main {
    public static void main(String[] args) throws InterruptedException {
        HashMap<String, Employee> map = new HashMap<>();
        Map<String, Employee> sortedMap = map.entrySet()
                                             .stream()
                                             .sorted(Entry.comparingByValue(Main::compare))
                                             .collect(Collectors.toMap(Entry::getKey, Entry::getValue,
                                                     (e1, e2) -> e1, LinkedHashMap::new));

    }

    public static int compare(Employee e1, Employee e2) {
        return e1.getAge() - e2.getAge();
    }
}

Edited: Here is another way you can use Comparator#comparing and thenComparing to sort.

Map<String, Employee> sortedMap = map.entrySet()
                                     .stream()
                                     .sorted(Entry.comparingByValue(
                                             Comparator.comparing(Employee::getAge)
                                                       .thenComparing(Employee::getSalary)))
                                     .collect(Collectors.toMap(Entry::getKey, Entry::getValue,
                                             (e1, e2) -> e1, LinkedHashMap::new));

7 Comments

This does not address the core of the question which is: how to compare on multiple properties.
Main:: compare is a comparator you can put your logic as your wish. You can use multiple properties to develop your logic in public static int compare(Employee e1, Employee e2) method.
Also sorting and then collecting to HashMap is useless since HashMap will neither retain sorting not insertion order. Use LinkedHashMap to retain insertion order. (Update: this was corrected in the answer later on.)
You are right I have updated my answer.
Yes, of course you can put logic for comparing multiple properties in Main.compare. But you did not - despite it being the core of the question.
|
0

HashMap is not an efficient data structure to sort by value. But if you really need to use it, you can try something like this:

map.entrySet().stream().sorted(Comparator.comparing(entry -> entry.getValue().param1)
                                                         .thenComparing(entry -> entry.getValue().param2)
                                                         .thenComparing(entry -> entry.getValue().param3))
        .collect(Collectors.toList());

1 Comment

Better comparingLong/thenComparingLong.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.