Sorting HashMap by values

495,397

Solution 1

Assuming Java, you could sort hashmap just like this:

public LinkedHashMap<Integer, String> sortHashMapByValues(
        HashMap<Integer, String> passedMap) {
    List<Integer> mapKeys = new ArrayList<>(passedMap.keySet());
    List<String> mapValues = new ArrayList<>(passedMap.values());
    Collections.sort(mapValues);
    Collections.sort(mapKeys);

    LinkedHashMap<Integer, String> sortedMap =
        new LinkedHashMap<>();

    Iterator<String> valueIt = mapValues.iterator();
    while (valueIt.hasNext()) {
        String val = valueIt.next();
        Iterator<Integer> keyIt = mapKeys.iterator();

        while (keyIt.hasNext()) {
            Integer key = keyIt.next();
            String comp1 = passedMap.get(key);
            String comp2 = val;

            if (comp1.equals(comp2)) {
                keyIt.remove();
                sortedMap.put(key, val);
                break;
            }
        }
    }
    return sortedMap;
}

Just a kick-off example. This way is more useful as it sorts the HashMap and keeps the duplicate values as well.

Solution 2

A generic version of a method to sort a Map resembles:

private static <K extends Comparable<K>, V extends Comparable<V>> Map<K, V> sort(
        final Map<K, V> unsorted,
        final boolean order) {
    final var list = new LinkedList<>(unsorted.entrySet());

    list.sort((o1, o2) -> order
                          ? o1.getValue().compareTo(o2.getValue()) == 0
                            ? o1.getKey().compareTo(o2.getKey())
                            : o1.getValue().compareTo(o2.getValue())
                          : o2.getValue().compareTo(o1.getValue()) == 0
                            ? o2.getKey().compareTo(o1.getKey())
                            : o2.getValue().compareTo(o1.getValue()));
    return list.stream().collect(
            Collectors.toMap(
                    Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new
            )
    );
}

The following code offers ascending and descending sorting by value:

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class SortMapByValue
{
    public static final boolean ASC = true;
    public static final boolean DESC = false;

    public static void main(String[] args)
    {

        // Creating dummy unsorted map
        Map<String, Integer> unsortMap = new HashMap<String, Integer>();
        unsortMap.put("B", 55);
        unsortMap.put("A", 80);
        unsortMap.put("D", 20);
        unsortMap.put("C", 70);

        System.out.println("Before sorting......");
        printMap(unsortMap);

        System.out.println("After sorting ascending order......");
        Map<String, Integer> sortedMapAsc = sortByComparator(unsortMap, ASC);
        printMap(sortedMapAsc);
        
        
        System.out.println("After sorting descindeng order......");
        Map<String, Integer> sortedMapDesc = sortByComparator(unsortMap, DESC);
        printMap(sortedMapDesc);

    }

    private static Map<String, Integer> sortByComparator(Map<String, Integer> unsortMap, final boolean order)
    {

        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(unsortMap.entrySet());

        // Sorting the list based on values
        Collections.sort(list, new Comparator<Entry<String, Integer>>()
        {
            public int compare(Entry<String, Integer> o1,
                    Entry<String, Integer> o2)
            {
                if (order)
                {
                    return o1.getValue().compareTo(o2.getValue());
                }
                else
                {
                    return o2.getValue().compareTo(o1.getValue());

                }
            }
        });

        // Maintaining insertion order with the help of LinkedList
        Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
        for (Entry<String, Integer> entry : list)
        {
            sortedMap.put(entry.getKey(), entry.getValue());
        }

        return sortedMap;
    }

    public static void printMap(Map<String, Integer> map)
    {
        for (Entry<String, Integer> entry : map.entrySet())
        {
            System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
        }
    }
}

Using newer Java features:

 import java.util.*;
 import java.util.Map.Entry;
 import java.util.stream.Collectors;
    
 public class SortMapByValue

 {
    private static final boolean ASC = true;
    private static final boolean DESC = false;
    public static void main(String[] args)
    {

        // Creating dummy unsorted map
        Map<String, Integer> unsortMap = new HashMap<>();
        unsortMap.put("B", 55);
        unsortMap.put("A", 20);
        unsortMap.put("D", 20);
        unsortMap.put("C", 70);

        System.out.println("Before sorting......");
        printMap(unsortMap);

        System.out.println("After sorting ascending order......");
        Map<String, Integer> sortedMapAsc = sortByValue(unsortMap, ASC);
        printMap(sortedMapAsc);


        System.out.println("After sorting descending order......");
        Map<String, Integer> sortedMapDesc = sortByValue(unsortMap, DESC);
        printMap(sortedMapDesc);
    }

    private static Map<String, Integer> sortByValue(Map<String, Integer> unsortMap, final boolean order)
    {
        List<Entry<String, Integer>> list = new LinkedList<>(unsortMap.entrySet());

        // Sorting the list based on values
        list.sort((o1, o2) -> order ? o1.getValue().compareTo(o2.getValue()) == 0
                ? o1.getKey().compareTo(o2.getKey())
                : o1.getValue().compareTo(o2.getValue()) : o2.getValue().compareTo(o1.getValue()) == 0
                ? o2.getKey().compareTo(o1.getKey())
                : o2.getValue().compareTo(o1.getValue()));
        return list.stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new));

    }

    private static void printMap(Map<String, Integer> map)
    {
        map.forEach((key, value) -> System.out.println("Key : " + key + " Value : " + value));
    }
}

Solution 3

In Java 8:

Map<Integer, String> sortedMap = 
     unsortedMap.entrySet().stream()
    .sorted(Entry.comparingByValue())
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue,
                              (e1, e2) -> e1, LinkedHashMap::new));

Solution 4

map.entrySet().stream()
                .sorted((k1, k2) -> -k1.getValue().compareTo(k2.getValue()))
                .forEach(k -> System.out.println(k.getKey() + ": " + k.getValue()));

Solution 5

You don't, basically. A HashMap is fundamentally unordered. Any patterns you might see in the ordering should not be relied on.

There are sorted maps such as TreeMap, but they traditionally sort by key rather than value. It's relatively unusual to sort by value - especially as multiple keys can have the same value.

Can you give more context for what you're trying to do? If you're really only storing numbers (as strings) for the keys, perhaps a SortedSet such as TreeSet would work for you?

Alternatively, you could store two separate collections encapsulated in a single class to update both at the same time?

Share:
495,397

Related videos on Youtube

prof_jack
Author by

prof_jack

Updated on March 15, 2022

Comments

  • prof_jack
    prof_jack over 2 years

    I need to sort my HashMap according to the values stored in it. The HashMap contains the contacts name stored in phone.

    Also I need that the keys get automatically sorted as soon as I sort the values, or you can say the keys and values are bound together thus any changes in values should get reflected in keys.

    HashMap<Integer,String> map = new HashMap<Integer,String>();
    map.put(1,"froyo");
    map.put(2,"abby");
    map.put(3,"denver");
    map.put(4,"frost");
    map.put(5,"daisy");
    

    Required output:

    2,abby;
    5,daisy;
    3,denver;
    4,frost;
    1,froyo;
    
  • prof_jack
    prof_jack over 12 years
    see the edited version.I don't think collections.sort(mapvalues) will solve the problem
  • prof_jack
    prof_jack over 12 years
    this code has arranged hashmap according to the keys.what I wanted was:(2,abby; 5,daisy; 3,denver; 4,frost; 1,froyo;)i.e values are arranged according to their initials and the change get reflected in the keys...
  • prof_jack
    prof_jack over 12 years
    I edited your code a bit and it is working as desired.thanks a tonne.
  • Rafael Sanches
    Rafael Sanches almost 11 years
    For example, sorting the colors that appear on an image. It has to be fast, because we can have max_int colors.
  • Jon Skeet
    Jon Skeet almost 11 years
    @RafaelSanches: It's not clear what the context for your comment is. What would the map be in this case anyway? You may want to ask a new question.
  • Rafael Sanches
    Rafael Sanches almost 11 years
    I'm just giving an example that would be useful to order the hashmap by values, in the most performant way.
  • Aditya
    Aditya over 10 years
    Nice answer, written it in a proper way using the collections utils.
  • Aditya
    Aditya over 10 years
    Tried for my problem and found that there needs to be a slight modification to your comparator logic, where you are not checking for null checks which should be added as map values can contain null elements.
  • Daniel F. Thornton
    Daniel F. Thornton over 10 years
    Please be sure your code runs correctly before answering. Also consider adding comments so the questioner can better understand your answer.
  • Admin
    Admin almost 9 years
    So great :) works 100 %
  • Kyzderp
    Kyzderp almost 9 years
    This only works if all the integer values are unique -- otherwise, you get strings overwritten.
  • Hengameh
    Hengameh almost 9 years
    Can we use something like this? Arrays.sort(hm.values().toArray());
  • Hengameh
    Hengameh almost 9 years
    Can we use something like this? Arrays.sort(hm.values().toArray());
  • Hengameh
    Hengameh almost 9 years
    Can we use something like this? Arrays.sort(hm.values().toArray());
  • Aquarius Power
    Aquarius Power over 8 years
    this sorting can be inverted? the values I need for from bigger to smaller
  • Vitalii Fedorenko
    Vitalii Fedorenko over 8 years
    @AquariusPower See the reversed method at docs.oracle.com/javase/8/docs/api/java/util/… You can apply it to the comparator returned by comparingByValue()
  • Aquarius Power
    Aquarius Power over 8 years
    the return of Entry.comparingByValue().reversed() is incompatible with .sorted(...) expected params :(, I ended up with a reversed loop for over .entrySet().toArray(), may be a cast could solve it, I need more time to test :)
  • Vitalii Fedorenko
    Vitalii Fedorenko over 8 years
    @AquariusPower you don't need casting, just give the compiler a hint on what the generic type is: Entry.<Integer, String>comparingByValue().reversed()
  • Radiodef
    Radiodef about 8 years
    This doesn't adhere to the Map interface. A proper implementation of entrySet() is: "Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa." Same thing for values().
  • picmate 涅
    picmate 涅 about 8 years
    Please be aware that the given algorithm has a time complexity of O(n^2) due to repeatedly looking up in values in two while loops. Converting the Entry Set to a List and then sorting the List based on a comparator would be a more efficient solution.
  • MayurB
    MayurB almost 8 years
    Map<Long,Employee> empidToEmployee; Sort map by employee salary.. How can we attach this problem.
  • Jon Skeet
    Jon Skeet almost 8 years
    @MayurB: I don't understand your comment at all, I'm afraid. But maps are usually unordered.
  • MayurB
    MayurB almost 8 years
    I have map of employeeID to Employee object.Employee object has name id, salary attributes. I want to sort the map by employee salary. This was a interview question asked to me.
  • Jon Skeet
    Jon Skeet almost 8 years
    @MayurB: Well normally even if a map is ordered, it's ordered by key in some way. With the context you've given, the question doesn't make much sense.
  • Andrei
    Andrei over 7 years
    @VitaliiFedorenko how can I mention comparator object in this syntax? My map is Map<SomeObject, Integer>, where do I inject the comparator there?
  • Andrei
    Andrei over 7 years
    Let me clarify my question. My map is Map<SomeObject, Integer>, and I want to sort by value desc, do I need to inject the comparator there?
  • Tanuj Verma
    Tanuj Verma over 7 years
    typos in it should be else if ((val1 != null) && (val2 != null)) { return val1.compareTo(val2); } in compare method
  • Shashank
    Shashank over 7 years
    Awesome just what i was looking for
  • 1813222
    1813222 about 7 years
    most readable solution for me and does the job, e.g. running over a HashMap sorted by values
  • NaveeNeo
    NaveeNeo almost 7 years
    I tried executing this code, runs fine with import java.util.Map.Entry;' But the same code does not run with import java.util.*; The later includes the former according to my knowledge. Then why it gives an error?
  • Srujan Barai
    Srujan Barai over 6 years
    What if I want to sort it by the length of the value, which for example is a String?
  • Justin Patel
    Justin Patel about 6 years
    Nice one. Thanks for this.
  • Caveman
    Caveman about 6 years
    Simple and elegant
  • hephestos
    hephestos about 6 years
    and this kind of programming exactly is the reason that I hate jdk8. getKey,getValue, (e1,e2)->e1, ::new... then we can summon "wooloh loh" around the fire and call the spirits of night...
  • Tushar Banne
    Tushar Banne almost 6 years
    if values are same, will the above code sort by keys?
  • Rais Alam
    Rais Alam almost 6 years
    @TusharBanne I have added new version which will support sorting based on key if values are same. Hope this will help you
  • Ram
    Ram almost 6 years
    Here List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(unsortMap.entrySet());, why not ArrayList?
  • Onic Team
    Onic Team about 5 years
    @JonSkeet: I am not getting your last line Can you please elaborate? Actually, I have student data example- name, rollNo, and 3 subject marks m1,m2,m3. Q - Sort Student data according to their total marks m1+m2+m3. Note - without using comparator and Comparable interface
  • Onic Team
    Onic Team about 5 years
    @JonSkeet: map.put(ss.getTotMarks(), ss); --- ss is my student object but if two student total marks are equal then older object replaced by new, I am using TreeMap
  • Jon Skeet
    Jon Skeet about 5 years
    @VedPrakash: Well yes, if you store a map using the total marks as the key, you shouldn't expect to be able to store two values with the same total marks. I suggest you ask a new question with more details about the problem you're facing.
  • Onic Team
    Onic Team about 5 years
    @JonSkeet: Sure
  • Onic Team
    Onic Team about 5 years
    @JonSkeet: As per your suggestion- I have asked a new Question on StackOverflow and waiting for the expected solution from StackOverflow because last hope is the StackOverflow - link - stackoverflow.com/questions/56352075/…
  • Vargan
    Vargan over 4 years
    The major problem with this approach is the quadratic time that it needs because of the nested while loop. There are better answers, like the excellent one from Rais Alarm down here.
  • Woden
    Woden about 4 years
    short and straightforward; elegant and readable.
  • Tuğrul Karakaya
    Tuğrul Karakaya almost 4 years
    ....sorted(Map.Entry.comparingByValue((a,b)-> b-a))...
  • Rohan
    Rohan almost 3 years
    this prints in the descending order of the values of map, how can i force it to ascending order?
  • givexa
    givexa almost 3 years
    @RaisAlam What instead of one single char as keys, we have words like AAA and AAB, how can we can we put it alphabetically then?
  • Kishy Nivas
    Kishy Nivas over 2 years
    remove the minus from k1.getValue() @Rohan