How to sort Map values by key in Java?

JavaDictionaryHashmap

Java Problem Overview


I have a Map that has strings for both keys and values.

Data is like following:
> "question1", "1"
> "question9", "1"
> "question2", "4"
>"question5", "2"

I want to sort the map based on its keys. So, in the end, I will have question1, question2, question3....and so on.


Eventually, I am trying to get two strings out of this Map.

  • First String: Questions ( in order 1 ..10)
  • Second String: Answers (in the same order as the question)

Right now I have the following:

Iterator it = paramMap.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry pairs = (Map.Entry) it.next();
    questionAnswers += pairs.getKey() + ",";
}

This gets me the questions in a string but they are not in order.

Java Solutions


Solution 1 - Java

Short answer

Use a TreeMap. This is precisely what it's for.

If this map is passed to you and you cannot determine the type, then you can do the following:

SortedSet<String> keys = new TreeSet<>(map.keySet());
for (String key : keys) { 
   String value = map.get(key);
   // do something
}

This will iterate across the map in natural order of the keys.


Longer answer

Technically, you can use anything that implements SortedMap, but except for rare cases this amounts to TreeMap, just as using a Map implementation typically amounts to HashMap.

For cases where your keys are a complex type that doesn't implement Comparable or you don't want to use the natural order then TreeMap and TreeSet have additional constructors that let you pass in a Comparator:

// placed inline for the demonstration, but doesn't have to be a lambda expression
Comparator<Foo> comparator = (Foo o1, Foo o2) -> {
        ...
    }

SortedSet<Foo> keys = new TreeSet<>(comparator);
keys.addAll(map.keySet());

Remember when using a TreeMap or TreeSet that it will have different performance characteristics than HashMap or HashSet. Roughly speaking operations that find or insert an element will go from O(1) to O(Log(N)).

In a HashMap, moving from 1000 items to 10,000 doesn't really affect your time to lookup an element, but for a TreeMap the lookup time will be about 1.3 times slower (assuming Log2). Moving from 1000 to 100,000 will be about 1.6 times slower for every element lookup.

Solution 2 - Java

Assuming TreeMap is not good for you (and assuming you can't use generics):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.

Solution 3 - Java

Using the TreeMap you can sort the map.

Map<String, String> map = new HashMap<>();        
Map<String, String> treeMap = new TreeMap<>(map);
for (String str : treeMap.keySet()) {
    System.out.println(str);
}

Solution 4 - Java

Just use TreeMap

new TreeMap<String, String>(unsortMap);

Be aware that the TreeMap is sorted according to the natural ordering of its 'keys'

Solution 5 - Java

Use a TreeMap!

Solution 6 - Java

If you already have a map and would like to sort it on keys, simply use :

Map<String, String> treeMap = new TreeMap<String, String>(yourMap);

A complete working example :

import java.util.HashMap;
import java.util.Set;
import java.util.Map;
import java.util.TreeMap;
import java.util.Iterator;

class SortOnKey {

public static void main(String[] args) {
   HashMap<String,String> hm = new HashMap<String,String>();
   hm.put("3","three");
   hm.put("1","one");
   hm.put("4","four");
   hm.put("2","two");
   printMap(hm);
   Map<String, String> treeMap = new TreeMap<String, String>(hm);
   printMap(treeMap);
}//main

public static void printMap(Map<String,String> map) {
    Set s = map.entrySet();
    Iterator it = s.iterator();
    while ( it.hasNext() ) {
       Map.Entry entry = (Map.Entry) it.next();
       String key = (String) entry.getKey();
       String value = (String) entry.getValue();
       System.out.println(key + " => " + value);
    }//while
    System.out.println("========================");
}//printMap

}//class

Solution 7 - Java

Provided you cannot use TreeMap, in Java 8 we can make use of toMap() method in Collectorswhich takes following parameters:

  • keymapper: mapping function to produce keys
  • valuemapper: mapping function to produce values
  • mergeFunction: a merge function, used to resolve collisions between values associated with the same key
  • mapSupplier: a function which returns a new, empty Map into which the results will be inserted.

Java 8 Example

Map<String,String> sample = new HashMap<>();  // push some values to map  
Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                    .sorted(Map.Entry.<String,String>comparingByKey().reversed())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
Map<String, String> newMapSortedByValue = sample.entrySet().stream()
                        .sorted(Map.Entry.<String,String>comparingByValue().reversed())
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));

We can modify the example to use custom comparator and to sort based on keys as:

Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                .sorted((e1,e2) -> e1.getKey().compareTo(e2.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));

Solution 8 - Java

Using Java 8:

Map<String, Integer> sortedMap = unsortMap.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));

Solution 9 - Java

In Java 8

To sort a Map<K, V> by key, putting keys into a List<K>:

List<K> result = map.keySet().stream().sorted().collect(Collectors.toList());

To sort a Map<K, V> by key, putting entries into a List<Map.Entry<K, V>>:

List<Map.Entry<K, V>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey())
       .collect(Collectors.toList());

Last but not least: to sort strings in a locale-sensitive manner - use a Collator (comparator) class:

Collator collator = Collator.getInstance(Locale.US);
collator.setStrength(Collator.PRIMARY); // case insensitive collator

List<Map.Entry<String, String>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey(collator))
       .collect(Collectors.toList());

Solution 10 - Java

This code can sort a key-value map in both orders i.e. ascending and descending.

<K, V extends Comparable<V>> Map<K, V> sortByValues
     (final Map<K, V> map, int ascending)
{
     Comparator<K> valueComparator =  new Comparator<K>() {	    	
        private int ascending;
        public int compare(K k1, K k2) {
        	int compare = map.get(k2).compareTo(map.get(k1));
        	if (compare == 0) return 1;
        	else return ascending*compare;
        }
        public Comparator<K> setParam(int ascending)
        {
          	this.ascending = ascending;
          	return this;
        }
    }.setParam(ascending);
        	    
    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

As an example:

Map<Integer,Double> recommWarrVals = new HashMap<Integer,Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals,-1);  // Descending order

Solution 11 - Java

List<String> list = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
for (String str : map.keySet()) {
 list.add(str);
}
Collections.sort(list);
for (String str : list) {
 System.out.println(str);
}

Solution 12 - Java

Just in case you don't wanna use a TreeMap

public static Map<Integer, Integer> sortByKey(Map<Integer, Integer> map) {
        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
        list.sort(Comparator.comparingInt(Map.Entry::getKey));
        Map<Integer, Integer> sortedMap = new LinkedHashMap<>();
        list.forEach(e -> sortedMap.put(e.getKey(), e.getValue()));
        return sortedMap;
    }

Also, in-case you wanted to sort your map on the basis of values just change Map.Entry::getKey to Map.Entry::getValue

Solution 13 - Java

In Java 8 you can also use .stream().sorted():

myMap.keySet().stream().sorted().forEach(key -> {
        String value = myMap.get(key);

        System.out.println("key: " + key);
        System.out.println("value: " + value);
    }
);

Solution 14 - Java

Use LinkedHashMap, which provide the key ordering. It's also gives same performance with HashMap. They both implements Map interface, so you can just replace the initialization object HashMap to LinkedHashMap.

Solution 15 - Java

How about below tree map:

Map<String, String> sortedMap = new TreeMap<>(Comparator.comparingInt(String::length)
.thenComparing(Function.identity()));

What ever you put in this sortedMap it will be sorted automatically. First of all TreeMap is sorted implementation of Map Interface. There is a but as it sorts keys on [natural order fashion][https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html]. As Java doc says String type is a Lexicographic Natural order type. Imagine the below list of numbers with String type. means below list will be sorted not as expected.

List<String> notSortedList = List.of("78","0", "24", "39", "4","53","32");

If you just you the default TreeMap constructor like below and push each element one-by-one like below:

    Map<String, String> map = new TreeMap<>();
    for (String s : notSortedList) {
        map.put(s, s);
    }


    System.out.println(map);

output is: {0=0, 14=14, 24=24, 32=32, 39=39, 4=4, 48=48, 53=53, 54=54, 78=78}

As you see number 4 for example comes after '39'. This is the nature of the Lexicographic data types like String. If that one was an Integer data type then that was okay though.

To fix this use argument to first check the length of the String and then compare them. In java 8 is done like this:

Map<String, String> sortedMap = new TreeMap<>(Comparator.comparingInt(String::length)
.thenComparing(Function.identity()));

It first compare each element by length then apply check by compareTo as the input the same as the element to compare with.

If you prefer to use a more understandable method, the above code will be equivalent with below code:

   Map<String, String> sortedMap = new TreeMap<>(
             new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    int lengthDifference = o1.length() - o2.length();
                    if (lengthDifference != 0) return lengthDifference;
                    return o1.compareTo(o2);
                }
            }
    );

Because the TreeMap constructor accepts the comparator Interface you can build up any even more complex implementation of Composite classes.

This is also another form more simplified version.

    Map<String,String> sortedMap = new TreeMap<>(
           (Comparator<String>) (o1, o2) ->
            {
                int lengthDifference = o1.length() - o2.length();
                if (lengthDifference != 0) return lengthDifference;
                return o1.compareTo(o2);
            }
    );

Solution 16 - Java

We can also sort the key by using Arrays.sort method.

Map<String, String> map = new HashMap<String, String>();
Object[] objArr = new Object[map.size()];
for (int i = 0; i < map.size(); i++) {
objArr[i] = map.get(i);
}
Arrays.sort(objArr);
for (Object str : objArr) {
System.out.println(str);
}

Solution 17 - Java

A good solution is provided here. We have a HashMap that stores values in unspecified order. We define an auxiliary TreeMap and we copy all data from HashMap into TreeMap using the putAll method. The resulting entries in the TreeMap are in the key-order.

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
Questionn00bstackieView Question on Stackoverflow
Solution 1 - JavaJhericoView Answer on Stackoverflow
Solution 2 - JavaTrayManView Answer on Stackoverflow
Solution 3 - JavaManoj SinghView Answer on Stackoverflow
Solution 4 - JavaAlitiView Answer on Stackoverflow
Solution 5 - JavaAgileJonView Answer on Stackoverflow
Solution 6 - JavaM-DView Answer on Stackoverflow
Solution 7 - Javaakhil_mittalView Answer on Stackoverflow
Solution 8 - JavaTaras MelnykView Answer on Stackoverflow
Solution 9 - JavaOleksandr PyrohovView Answer on Stackoverflow
Solution 10 - JavaMorteza MashayekhiView Answer on Stackoverflow
Solution 11 - JavaManoj SinghView Answer on Stackoverflow
Solution 12 - JavaAnkit SharmaView Answer on Stackoverflow
Solution 13 - JavaJonas_HessView Answer on Stackoverflow
Solution 14 - JavaiamdualView Answer on Stackoverflow
Solution 15 - JavamatioView Answer on Stackoverflow
Solution 16 - JavaManoj SinghView Answer on Stackoverflow
Solution 17 - JavaSandeepView Answer on Stackoverflow