452

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

The data is like the 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.

khelwood
  • 55,782
  • 14
  • 81
  • 108
n00bstackie
  • 4,793
  • 5
  • 19
  • 9
  • Provided you cannot use TreeMap, in Java 8 we can make use of toMap() method : https://stackoverflow.com/a/40649809/1216775 – akhil_mittal Oct 18 '21 at 04:41

18 Answers18

722

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.

Jherico
  • 28,584
  • 8
  • 61
  • 87
  • 1
    I'm trying to use Treemap and sort String keys based on length. I find I'm getting inconsistent retrieval results. Apparently because TreeMap considers a compareTo result of 0 as "equals"? Not sure how to use it in this case. – Marc Aug 16 '13 at 16:35
  • 2
    compareTo() result of 0 is 'equals'. If you're writing a comparator that sorts by string length, then you need to return a positive or negative value based on which string is longer, and only return 0 if both strings are the same length. if a and b are strings you can do this like so `return a.length() - b.length()' (or reverse the values if you want them sorted in the other direction). – Jherico Aug 16 '13 at 20:15
  • Hello guys, if he/she would to the Map to be orderd by keys, which here is 1,2,3,4 , what is the insertation order.... why not we using LinkedHashSet ? We just put the questions one by one, and it is getting ordered by the order of the insertation. Can some help me out with this ? – narancs Jun 19 '15 at 13:15
  • @Karoly LinkedHashSet would work to retrieve the elements in the order you inserted them. What the OP is wanting is to retrieve the elements in some predetermined sort order, irrespective of insertion order. – David Berry Nov 13 '15 at 16:28
  • @cricket_007 the code is demonstrating specifically how to iterate across they keys for a map that isn't already sorted. – Jherico Mar 02 '16 at 17:47
  • 1
    log₂(1000)≈10, log₂(10000)≈13 (rounding). That's 1.3 times slower, not 3 times! – Walter Tross May 04 '16 at 10:12
160

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.
TrayMan
  • 7,180
  • 3
  • 24
  • 33
79

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);
}
Lii
  • 11,553
  • 8
  • 64
  • 88
Manoj Singh
  • 857
  • 6
  • 2
  • 1
    Map> treeMap = new TreeMap>(printHashMap); for (String str : treeMap.keySet()) { System.out.println(str + " " + treeMap.get(str)); } – vikramvi Aug 21 '16 at 15:40
55

Just use TreeMap:

new TreeMap<String, String>(unsortMap);

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Aliti
  • 2,025
  • 2
  • 27
  • 39
42

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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
M-D
  • 10,247
  • 9
  • 32
  • 35
42

Use a TreeMap!

Ben
  • 54,723
  • 49
  • 178
  • 224
AgileJon
  • 53,070
  • 5
  • 41
  • 38
32

Provided you cannot use TreeMap, in Java 8 we can make use of the toMap() method in Collectors which takes the 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));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
akhil_mittal
  • 23,309
  • 7
  • 96
  • 95
22

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));
Taras Melnyk
  • 3,057
  • 3
  • 38
  • 34
20

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());
Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
5

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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Morteza Mashayekhi
  • 934
  • 11
  • 23
2

Just in case you don't want to 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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ankit Sharma
  • 1,626
  • 1
  • 14
  • 21
  • 1
    This came up in my review log. Someone tried to fix some errors with variables names (but failed). So, I fixed them correctly, however, this answer is just wrong fundamentally. The order in which you add values to a HashMap is irrelevant. Maps do not have order. https://stackoverflow.com/questions/10710193/how-to-preserve-insertion-order-in-hashmap – aepryus Feb 24 '20 at 04:38
  • What is `e` variable at your source code? `(e.getKey(), e.getValue())` – Max Base Sep 20 '20 at 18:42
  • @aepryus The answer is not wrong. The order of insertion order in LinkedHashMap is relevant. That's the main point of having the LinkedHashMap. The order of insertion is irrelevant for the HashMaps. – Sergey Sargsyan Oct 02 '22 at 12:12
  • 1
    @SergeySargsyan It appears the answerer changed to a LInkedHashMap 6 months after my comment. I'm unfamiliar with LinkedHashMap, but I'll take you word for it that my comment is no longer valid. – aepryus Oct 02 '22 at 12:46
2
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);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Manoj Singh
  • 857
  • 6
  • 2
1

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);
    }
);
Jonas_Hess
  • 1,874
  • 1
  • 22
  • 32
1

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.

Sandeep
  • 1,245
  • 1
  • 13
  • 33
1

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

iamdual
  • 1,251
  • 14
  • 11
  • `LinkedHashMap` was enough for me, since I already have a sorted collection of keys and I am inserting them in order too, no need of implementing any Comparable/Comparator interface – FiruzzZ Nov 26 '21 at 12:51
  • 1
    Re *"the same performance with HashMap"*: Do you mean *"the same performance* ***as*** *HashMap"* – Peter Mortensen Aug 15 '22 at 15:02
1

Use the below tree map:

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

Whatever 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 in the natural order fashion. As the Java documentation says, String type is a lexicographic natural order type. Imagine the below list of numbers with the String type. It means the below list will not be sorted as expected.

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

If you just use 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);

The 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 an argument to first check the length of the String and then compare them. In Java 8 it is done like this:

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

It first compares 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 the below code:

Map<String, String> sortedMap = new TreeMap<>( new Comparator() { @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 an even more complex implementation of Composite classes.

This is also another form for a simpler 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);
    }
);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
matio
  • 397
  • 2
  • 14
0

Using TreeMap

Let's initialize a map with some random key-value pairs

Map<String, String> map = Map.of("key5", "value5",
            "key2", "value2",
            "key4", "value4",
            "key1", "value1",
            "key3", "value3");

Sort by map's key in ascending order

Map<String, String> sortedTreeMap = new TreeMap<>(map);

System.out.println(sortedTreeMap);
// {key1=value1, key2=value2, key3=value3, key4=value4, key5=value5}

Sort by map's key in descending order

Map<String, String> reverseSortedTreeMap = new TreeMap<>(Comparator.reverseOrder());
reverseSortedTreeMap.putAll(map);

System.out.print(reverseSortedTreeMap);
// {key5=value5, key4=value4, key3=value3, key2=value2, key1=value1}

Source: Sort Map by Key using TreeMap in Java

Ashish Lahoti
  • 648
  • 6
  • 8
-1

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);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Manoj Singh
  • 857
  • 6
  • 2