Sorting HashMap by values

JavaSortingHashmap

Java Problem Overview


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;

Java Solutions


Solution 1 - Java

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 boolean ASC = true;
	public static 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 boolean ASC = true;
    private static 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 2 - Java

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 3 - Java

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 4 - Java

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

Solution 5 - Java

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?

Solution 6 - Java

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());

		}

	}
}

Solution 7 - Java

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.

Solution 8 - Java

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());
    }

}

Solution 9 - Java

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());
            } 
        }
    }

Solution 10 - Java

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);
}
}

Solution 11 - Java

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);
}

Solution 12 - Java

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;
		}
	}

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionprof_jackView Question on Stackoverflow
Solution 1 - JavaRais AlamView Answer on Stackoverflow
Solution 2 - JavaVitalii FedorenkoView Answer on Stackoverflow
Solution 3 - JavaSandeep PathakView Answer on Stackoverflow
Solution 4 - JavaOla PolaView Answer on Stackoverflow
Solution 5 - JavaJon SkeetView Answer on Stackoverflow
Solution 6 - JavaNAVEEN KumarView Answer on Stackoverflow
Solution 7 - JavakolyasegView Answer on Stackoverflow
Solution 8 - JavaWendelView Answer on Stackoverflow
Solution 9 - JavalannyfView Answer on Stackoverflow
Solution 10 - JavaHakunaMatataView Answer on Stackoverflow
Solution 11 - JavaNdroidView Answer on Stackoverflow
Solution 12 - JavalohithView Answer on Stackoverflow