164

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;
Jake OS
  • 180
  • 1
  • 16
prof_jack
  • 2,023
  • 3
  • 18
  • 19

12 Answers12

189

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));
    }
}
Simon Baars
  • 1,877
  • 21
  • 38
Rais Alam
  • 6,970
  • 12
  • 53
  • 84
  • 4
    Nice answer, written it in a proper way using the collections utils. – Aditya Dec 28 '13 at 09:32
  • 2
    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. – Aditya Dec 28 '13 at 10:09
  • What if I want to sort it by the length of the `value`, which for example is a String? – Srujan Barai Nov 12 '17 at 03:58
  • if values are same, will the above code sort by keys? – Tushar Banne Jul 12 '18 at 18:59
  • @TusharBanne I have added new version which will support sorting based on key if values are same. Hope this will help you – Rais Alam Jul 15 '18 at 07:16
  • Here `List> list = new LinkedList>(unsortMap.entrySet());`, why not `ArrayList`? – Ram Aug 24 '18 at 17:15
  • @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? – givexa Aug 04 '21 at 14:35
120

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));
Vitalii Fedorenko
  • 110,878
  • 29
  • 149
  • 111
  • Can we use something like this? `Arrays.sort(hm.values().toArray());` – Hengameh Aug 28 '15 at 14:52
  • 1
    this sorting can be inverted? the values I need for from bigger to smaller – Aquarius Power Dec 18 '15 at 19:28
  • 3
    @AquariusPower See the `reversed` method at https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#reversed-- You can apply it to the comparator returned by `comparingByValue()` – Vitalii Fedorenko Dec 28 '15 at 00:56
  • 4
    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 :) – Aquarius Power Dec 28 '15 at 15:41
  • 5
    @AquariusPower you don't need casting, just give the compiler a hint on what the generic type is: `Entry.comparingByValue().reversed()` – Vitalii Fedorenko Dec 28 '15 at 18:36
  • @VitaliiFedorenko how can I mention comparator object in this syntax? My map is `Map`, where do I inject the comparator there? – Andrei Feb 15 '17 at 22:21
  • Let me clarify my question. My map is `Map`, and I want to sort by value desc, do I need to inject the comparator there? – Andrei Feb 15 '17 at 22:31
  • 11
    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... – hephestos May 23 '18 at 19:46
  • 1
    ....sorted(Map.Entry.comparingByValue((a,b)-> b-a))... – Tuğrul Karakaya Sep 15 '20 at 14:18
  • Getting error "Cannot resolve symbol 'Entry'", how to fix this ? – vikramvi Jul 12 '23 at 01:59
100

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.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
Sandeep Pathak
  • 10,567
  • 8
  • 45
  • 57
  • see the edited version.I don't think collections.sort(mapvalues) will solve the problem – prof_jack Nov 14 '11 at 09:32
  • 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 Nov 14 '11 at 10:06
  • So great :) works 100 % –  Jul 08 '15 at 09:07
  • Can we use something like this? `Arrays.sort(hm.values().toArray());` – Hengameh Aug 28 '15 at 14:51
  • 29
    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. – picmate 涅 May 21 '16 at 02:53
  • 1
    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. – Vargan Jan 29 '20 at 22:36
  • What an absolute faff looking at all these answers. In C# just use a dictionary and dict.OrderByDescending(a => a.Value); piece of cake. Java is so verbose and stuck in the dark ages – Blingers Jan 04 '23 at 18:17
39
map.entrySet().stream()
                .sorted((k1, k2) -> -k1.getValue().compareTo(k2.getValue()))
                .forEach(k -> System.out.println(k.getKey() + ": " + k.getValue()));
Ola Pola
  • 391
  • 3
  • 2
26

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?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    For example, sorting the colors that appear on an image. It has to be fast, because we can have max_int colors. – Rafael Sanches Sep 28 '13 at 11:12
  • @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. – Jon Skeet Sep 28 '13 at 11:14
  • 1
    I'm just giving an example that would be useful to order the hashmap by values, in the most performant way. – Rafael Sanches Sep 29 '13 at 11:53
  • Can we use something like this? `Arrays.sort(hm.values().toArray());` – Hengameh Aug 28 '15 at 14:52
  • Map empidToEmployee; Sort map by employee salary.. How can we attach this problem. – MayurB Aug 01 '16 at 13:42
  • @MayurB: I don't understand your comment at all, I'm afraid. But maps are *usually* unordered. – Jon Skeet Aug 01 '16 at 21:38
  • 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. – MayurB Aug 02 '16 at 10:32
  • @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. – Jon Skeet Aug 02 '16 at 11:03
  • @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 May 28 '19 at 17:15
  • @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 – Onic Team May 28 '19 at 17:18
  • 1
    @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. – Jon Skeet May 28 '19 at 18:12
  • @JonSkeet: Sure – Onic Team May 29 '19 at 01:23
  • @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 - https://stackoverflow.com/questions/56352075/how-to-sort-map-without-using-comparable-and-comparator-interface – Onic Team May 29 '19 at 02:04
24
package com.naveen.hashmap;

import java.util.*;
import java.util.Map.Entry;

public class SortBasedonValues {

    /**
     * @param args
     */
    public static void main(String[] args) {

        HashMap<String, Integer> hm = new HashMap<String, Integer>();
        hm.put("Naveen", 2);
        hm.put("Santosh", 3);
        hm.put("Ravi", 4);
        hm.put("Pramod", 1);
        Set<Entry<String, Integer>> set = hm.entrySet();
        List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(
                set);
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
            public int compare(Map.Entry<String, Integer> o1,
                    Map.Entry<String, Integer> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });

        for (Entry<String, Integer> entry : list) {
            System.out.println(entry.getValue());

        }

    }
}
ammar26
  • 1,584
  • 4
  • 21
  • 41
NAVEEN Kumar
  • 347
  • 2
  • 4
  • 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? – NaveeNeo Aug 17 '17 at 12:03
  • Simple and elegant – Caveman May 05 '18 at 18:33
12

As a kind of simple solution you can use temp TreeMap if you need just a final result:

TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>();
for (Map.Entry entry : map.entrySet()) {
    sortedMap.put((String) entry.getValue(), (Integer)entry.getKey());
}

This will get you strings sorted as keys of sortedMap.

kolyaseg
  • 535
  • 5
  • 13
  • 30
    This only works if all the integer values are unique -- otherwise, you get strings overwritten. – Kyzderp Aug 04 '15 at 00:23
5

I extends a TreeMap and override entrySet() and values() methods. Key and value need to be Comparable.

Follow the code:

public class ValueSortedMap<K extends Comparable, V extends Comparable> extends TreeMap<K, V> {

    @Override
    public Set<Entry<K, V>> entrySet() {
        Set<Entry<K, V>> originalEntries = super.entrySet();
        Set<Entry<K, V>> sortedEntry = new TreeSet<Entry<K, V>>(new Comparator<Entry<K, V>>() {
            @Override
            public int compare(Entry<K, V> entryA, Entry<K, V> entryB) {
                int compareTo = entryA.getValue().compareTo(entryB.getValue());
                if(compareTo == 0) {
                    compareTo = entryA.getKey().compareTo(entryB.getKey());
                }
                return compareTo;
            }
        });
        sortedEntry.addAll(originalEntries);
        return sortedEntry;
    }

    @Override
    public Collection<V> values() {
        Set<V> sortedValues = new TreeSet<>(new Comparator<V>(){
            @Override
            public int compare(V vA, V vB) {
                return vA.compareTo(vB);
            }
        });
        sortedValues.addAll(super.values());
        return sortedValues;
    }
}

Unit Tests:

public class ValueSortedMapTest {

    @Test
    public void basicTest() {
        Map<String, Integer> sortedMap = new ValueSortedMap<>();
        sortedMap.put("A",3);
        sortedMap.put("B",1);
        sortedMap.put("C",2);

        Assert.assertEquals("{B=1, C=2, A=3}", sortedMap.toString());
    }

    @Test
    public void repeatedValues() {
        Map<String, Double> sortedMap = new ValueSortedMap<>();
        sortedMap.put("D",67.3);
        sortedMap.put("A",99.5);
        sortedMap.put("B",67.4);
        sortedMap.put("C",67.4);

        Assert.assertEquals("{D=67.3, B=67.4, C=67.4, A=99.5}", sortedMap.toString());
    }

}
Wendel
  • 2,809
  • 29
  • 28
  • 1
    This doesn't adhere to the `Map` interface. A proper implementation of [`entrySet()`](http://docs.oracle.com/javase/8/docs/api/java/util/Map.html#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()`. – Radiodef Apr 18 '16 at 23:07
  • Awesome just what i was looking for – Shashank Mar 24 '17 at 12:57
2

found a solution but not sure the performance if the map has large size, useful for normal case.

   /**
     * sort HashMap<String, CustomData> by value
     * CustomData needs to provide compareTo() for comparing CustomData
     * @param map
     */

    public void sortHashMapByValue(final HashMap<String, CustomData> map) {
        ArrayList<String> keys = new ArrayList<String>();
        keys.addAll(map.keySet());
        Collections.sort(keys, new Comparator<String>() {
            @Override
            public int compare(String lhs, String rhs) {
                CustomData val1 = map.get(lhs);
                CustomData val2 = map.get(rhs);
                if (val1 == null) {
                    return (val2 != null) ? 1 : 0;
                } else if (val1 != null) && (val2 != null)) {
                    return = val1.compareTo(val2);
                }
                else {
                    return 0;
                }
            }
        });

        for (String key : keys) {
            CustomData c = map.get(key);
            if (c != null) {
                Log.e("key:"+key+", CustomData:"+c.toString());
            } 
        }
    }
Aniket Thakur
  • 66,731
  • 38
  • 279
  • 289
lannyf
  • 9,865
  • 12
  • 70
  • 152
  • typos in it should be else if ((val1 != null) && (val2 != null)) { return val1.compareTo(val2); } in compare method – Tanuj Verma Feb 20 '17 at 05:23
0
package SortedSet;

import java.util.*;

public class HashMapValueSort {
public static void main(String[] args){
    final Map<Integer, String> map = new HashMap<Integer,String>();
    map.put(4,"Mango");
    map.put(3,"Apple");
    map.put(5,"Orange");
    map.put(8,"Fruits");
    map.put(23,"Vegetables");
    map.put(1,"Zebra");
    map.put(5,"Yellow");
    System.out.println(map);
    final HashMapValueSort sort = new HashMapValueSort();
    final Set<Map.Entry<Integer, String>> entry = map.entrySet();
    final Comparator<Map.Entry<Integer, String>> comparator = new Comparator<Map.Entry<Integer, String>>() {
        @Override
        public int compare(Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) {
            String value1 = o1.getValue();
            String value2 = o2.getValue();
            return value1.compareTo(value2);
        }
    };
    final SortedSet<Map.Entry<Integer, String>> sortedSet = new TreeSet(comparator);
    sortedSet.addAll(entry);
    final Map<Integer,String> sortedMap =  new LinkedHashMap<Integer, String>();
    for(Map.Entry<Integer, String> entry1 : sortedSet ){
        sortedMap.put(entry1.getKey(),entry1.getValue());
    }
    System.out.println(sortedMap);
}
}
HakunaMatata
  • 814
  • 2
  • 12
  • 22
0
public static TreeMap<String, String> sortMap(HashMap<String, String> passedMap, String byParam) {
    if(byParam.trim().toLowerCase().equalsIgnoreCase("byValue")) {
        // Altering the (key, value) -> (value, key)
        HashMap<String, String> newMap =  new HashMap<String, String>();
        for (Map.Entry<String, String> entry : passedMap.entrySet()) {
            newMap.put(entry.getValue(), entry.getKey());
        }
        return new TreeMap<String, String>(newMap);
    }
    return new TreeMap<String, String>(passedMap);
}
linski
  • 5,046
  • 3
  • 22
  • 35
Ndroid
  • 449
  • 4
  • 5
-2
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

public class CollectionsSort {

    /**
     * @param args
     */`enter code here`
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        CollectionsSort colleciotns = new CollectionsSort();

        List<combine> list = new ArrayList<combine>();
        HashMap<String, Integer> h = new HashMap<String, Integer>();
        h.put("nayanana", 10);
        h.put("lohith", 5);

        for (Entry<String, Integer> value : h.entrySet()) {
            combine a = colleciotns.new combine(value.getValue(),
                    value.getKey());
            list.add(a);
        }

        Collections.sort(list);
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

    public class combine implements Comparable<combine> {

        public int value;
        public String key;

        public combine(int value, String key) {
            this.value = value;
            this.key = key;
        }

        @Override
        public int compareTo(combine arg0) {
            // TODO Auto-generated method stub
            return this.value > arg0.value ? 1 : this.value < arg0.value ? -1
                    : 0;
        }

        public String toString() {
            return this.value + " " + this.key;
        }
    }

}
lohith
  • 9
  • 1
  • Please be sure your code runs correctly before answering. Also consider adding comments so the questioner can better understand your answer. – Daniel F. Thornton Feb 05 '14 at 19:26