387

Is there an object in Java that acts like a Map for storing and accessing key/value pairs, but can return an ordered list of keys and an ordered list of values, such that the key and value lists are in the same order?

So as explanation-by-code, I'm looking for something that behaves like my fictitious OrderedMap:

OrderedMap<Integer, String> om = new OrderedMap<>();
om.put(0, "Zero");
om.put(7, "Seven");

String o = om.get(7); // o is "Seven"
List<Integer> keys = om.getKeys();
List<String> values = om.getValues();

for(int i = 0; i < keys.size(); i++)
{
    Integer key = keys.get(i);
    String value = values.get(i);
    Assert(om.get(key) == value);
}
kometen
  • 6,536
  • 6
  • 41
  • 51
Whatsit
  • 10,227
  • 11
  • 42
  • 41
  • 4
    If all you're wanting to do is iterating through both at the same time, then Map.entrySet() will let you do that on any map. The LinkedHashMap has a well defined order, but for any Map the entry set reflects the key/value pairs. – Pete Kirkham Mar 19 '09 at 18:24
  • 5
    This code is not a good example as any Map implementation will behave as your sample code. sorted, ordered or not. – Peter Lawrey Mar 19 '09 at 20:42
  • Peter Lawrey: Could you expand upon that? The Map interface returns keys and values as a Set and Collection, respectively. Order is not guaranteed in either of these, so it doesn't seem sensible to say that every Map implementation will behave as in my example code. – Whatsit Mar 20 '09 at 15:27
  • 2
    In the Sun JDK implementation, the sets returned by getKeys and getValues() sets are backed by the entrySet() in the map, so will have the same iteration order, which is what your sample tests. – Pete Kirkham Mar 20 '09 at 17:34
  • 4
    Well that's interesting, I never noticed that. Still, call me crazy, but I prefer not to make assumptions about implementation that aren't explicitly verified by the interface. I've been burned too many times doing that in the past. – Whatsit Mar 20 '09 at 19:00
  • 2
    This should be named Java Sorted Map, as Ordered Map is something different - see `LinkedHashMap`. – Ondra Žižka Jan 16 '10 at 10:18
  • [SortedMap](http://java.sun.com/javase/6/docs/api/java/util/SortedMap.html) – Ichorus Mar 19 '09 at 18:17
  • Possible duplicate of [Java Class that implements Map and keeps insertion order?](https://stackoverflow.com/questions/683518/java-class-that-implements-map-and-keeps-insertion-order) – Organic Advocate Jul 07 '17 at 17:34

10 Answers10

481

The SortedMap interface (with the implementation TreeMap) should be your friend.

The interface has the methods:

  • keySet() which returns a set of the keys in ascending order
  • values() which returns a collection of all values in the ascending order of the corresponding keys

So this interface fulfills exactly your requirements. However, the keys must have a meaningful order. Otherwise you can used the LinkedHashMap where the order is determined by the insertion order.

Laurel
  • 5,965
  • 14
  • 31
  • 57
dmeister
  • 34,704
  • 19
  • 73
  • 95
  • 2
    example: SortedMap map = new TreeMap<>(); – Ben Jan 22 '17 at 22:11
  • 14
    To use TreeMap it required that key class have to implement Comparable interface. If not, then some kind of RuntimeException will be thrown. TreeMap it's also sorted map, but I think author want to use just ordered (not sorted) map. LinkedHashMap it's good choice to get only ordered map (as You said, "determined by insertion order"). – K. Gol Jan 24 '17 at 08:01
  • `TreeMap` _does not_ require the key class to implement `Comparable` if you give it a suitable `Comparator` [during construction](https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html#TreeMap-java.util.Comparator-) (assuming you don't use `Comparator.naturalOrder()`). – Slaw Jul 14 '18 at 04:59
  • 1
    this answer could be improved by showing how to iterate over keySet() –  Jan 29 '19 at 07:55
  • 8
    From [java 8 doc](https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html). `LinkedHashMap` whose order of iteration is the **order in which its entries were last accessed** – TRiNE May 15 '19 at 09:45
  • 6
    @TRiNE I don't follow your comment, but I may have missed some context. By default `LinkedHashMap`'s iteration order is insertion-order, but you can use a different constructor to specify access-order instead. https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html#LinkedHashMap-int-float-boolean- – rob Jun 11 '19 at 15:29
  • @rob yes agree. – TRiNE Jun 11 '19 at 18:48
  • If `SortedMap.keySet()` returns ordered keys, why not return a `List` instead of a `Set`? I can't do **anything** with the freaking set, can't even get a first element (without resorting to some weird bs loop tricks). – parsecer Aug 09 '19 at 12:03
  • @parsecer: Because SortedMap is a specialication of Map, which returns a Set, since there is no order and the information that each element is unique is more valuable. You can (most likely) cast (or transform) that `Set` in a `SortedSet`, where you have a `first()` again. You can also use `SortedMap.firstKey()` for that. – derM - not here for BOT dreams Aug 26 '19 at 14:08
  • OP asks about ordering, answer is about sorting – Monish Sen Mar 19 '23 at 10:59
250

Is there an object that acts like a Map for storing and accessing key/value pairs, but can return an ordered list of keys and an ordered list of values, such that the key and value lists are in the same order?

You're looking for java.util.LinkedHashMap. You'll get a list of Map.Entry<K,V> pairs, which always get iterated in the same order. That order is the same as the order by which you put the items in. Alternatively, use the java.util.SortedMap, where the keys must either have a natural ordering or have it specified by a Comparator.

John Feminella
  • 303,634
  • 46
  • 339
  • 357
  • 20
    And to just save the reader double checking this, because it's hard to verify by testing, the `keySet()` method effectively returns a LinkedHashSet which reflects the order of your `put()` calls. Note that repeated calls to `put()` for the same key will not change the order unless you `remove()` the key beforehand. – Glenn Lawrence Mar 03 '15 at 03:29
  • 1
    @TRiNE quoting from the linked docs: "... which is normally the order in which keys were inserted into the map". The iteration order is always the "insertion-order", unless you use a [special constructor](https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html#LinkedHashMap-int-float-boolean-) asking for "access-order" – Qw3ry Oct 23 '20 at 08:41
43

LinkedHashMap maintains the order of the keys.

java.util.LinkedHashMap appears to work just like a normal HashMap otherwise.

VoNWooDSoN
  • 1,173
  • 9
  • 13
  • 1
    This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](http://stackoverflow.com/help/privileges/comment). – ianaya89 Jan 06 '15 at 00:29
  • 3
    @ianaya89 I think this is a real answer, but It's very similar to [John Feminella's](http://stackoverflow.com/a/663388/1677209) answer! – T30 Mar 06 '15 at 11:15
  • 1
    If you want to get a ordered map, where the entries are stored in that order as you put them into the map, than a LinkedHashMap is the right answer. If you want to sort the entries in your map independent form the order you put them in, than a SortedMap is the right answer. – Ralph Jun 04 '15 at 11:31
  • @TRiNE The java 8 doc you linked says there are two ordering modes possible: insertion-order and access-order. You think about the latter which is invoked by the use of a special constructor public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder). Default constructor creates an insertion-ordered LinkedHashMap instance. – Artur Łysik Feb 13 '20 at 08:51
  • 1
    @ArturŁysik yes it is. It was a mistake done by me days ago. I'll correct it. I'm deleting the comment. as I no longer can edit it – TRiNE Feb 14 '20 at 09:07
8

tl;dr

To keep Map< Integer , String > in an order sorted by key, use either of the two classes implementing the SortedMap/NavigableMap interfaces:

… or third-party implementations. Perhaps in Google Guava or Eclipse Collections (I’ve not checked).

If manipulating the map within a single thread, use the first, TreeMap. If manipulating across threads, use the second, ConcurrentSkipListMap.

For details, see the table below and the following discussion.

Details

Here is a graphic table I made showing the features of the ten Map implementations bundled with Java 11.

The NavigableMap interface is the successor to SortedMap. The SortedMap logically should be removed but cannot be as some 3rd-party map implementations may be using interface.

As you can see in this table, only two classes implement the SortedMap/NavigableMap interfaces:

Both of these keep keys in sorted order, either by their natural order (using compareTo method of the Comparable(https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Comparable.html) interface) or by a Comparator implementation you pass. The difference between these two classes is that the second one, ConcurrentSkipListMap, is thread-safe, highly concurrent.

See also the Iteration order column in the table below.

  • The LinkedHashMap class returns its entries by the order in which they were originally inserted.
  • EnumMap returns entries in the order by which the enum class of the key is defined. For example, a map of which employee is covering which day of the week (Map< DayOfWeek , Person >) uses the DayOfWeek enum class built into Java. That enum is defined with Monday first and Sunday last. So entries in an iterator will appear in that order.

The other six implementations make no promise about the order in which they report their entries.

Table of map implementations in Java 11, comparing their features

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
7

You can leverage NavigableMap interface that may be accessed and traversed in either ascending or descending key order. This interface is intended to supersede the SortedMap interface. The Navigable map is usually sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time.

There are three most useful implementations of it: TreeMap, ImmutableSortedMap, and ConcurrentSkipListMap.

TreeMap example:

TreeMap<String, Integer> users = new TreeMap<String, Integer>();
users.put("Bob", 1);
users.put("Alice", 2);
users.put("John", 3);

for (String key: users.keySet()) {
  System.out.println(key + " (ID = "+ users.get(key) + ")");
}

Output:

Alice (ID = 2)
Bob (ID = 1)
John (ID = 3)
Vitalii Fedorenko
  • 110,878
  • 29
  • 149
  • 111
6

I think the closest collection you'll get from the framework is the SortedMap

bruno conde
  • 47,767
  • 15
  • 98
  • 117
  • 4
    I would down vote this answer if I thought it was worth losing the points for it. As the above answer points out, your answer lacks the proper information about LinkedHashMap, and a little explanation of SortedMap would be nice too. – CorayThan Nov 06 '13 at 18:06
  • @CorayThan, in that case you upvote the best answers, not downvote others that may be correct but not the best... – bruno conde Nov 08 '13 at 17:43
  • 2
    That's what I did. Just saying I can understand why someone would down vote it. – CorayThan Nov 08 '13 at 18:08
4

Since Java 6 there is also non-blocking thread-safe alternative to TreeMap. See ConcurrentSkipListMap.

Vadzim
  • 24,954
  • 11
  • 143
  • 151
4

I think the SortedMap interface enforces what you ask for and TreeMap implements that.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/SortedMap.html http://java.sun.com/j2se/1.5.0/docs/api/java/util/TreeMap.html

CJ F
  • 835
  • 2
  • 8
  • 22
0

I have used Simple Hash map, linked list and Collections to sort a Map by values.

import java.util.*;
import java.util.Map.*;
public class Solution {

    public static void main(String[] args) {
        // create a simple hash map and insert some key-value pairs into it
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("Python", 3);
        map.put("C", 0);
        map.put("JavaScript", 4);
        map.put("C++", 1);
        map.put("Golang", 5);
        map.put("Java", 2);
        // Create a linked list from the above map entries
        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
        // sort the linked list using Collections.sort()
        Collections.sort(list, new Comparator<Entry<String, Integer>>(){
        @Override
         public int compare(Entry<String, Integer> m1, Entry<String, Integer> m2) {
        return m1.getValue().compareTo(m2.getValue());
        }
      });
      for(Entry<String, Integer> value: list) {
         System.out.println(value);
     }
   }
}

The output is:

C=0
C++=1
Java=2
Python=3
JavaScript=4
Golang=5
Steffi Keran Rani J
  • 3,667
  • 4
  • 34
  • 56
0

Modern Java version of Steffi Keran's answer

public class Solution {
    public static void main(String[] args) {
        // create a simple hash map and insert some key-value pairs into it
        Map<String, Integer> map = new HashMap<>();
        map.put("Python", 3);
        map.put("C", 0);
        map.put("JavaScript", 4);
        map.put("C++", 1);
        map.put("Golang", 5);
        map.put("Java", 2);
        // Create a linked list from the above map entries
        List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
        // sort the linked list using Collections.sort()
        list.sort(Comparator.comparing(Map.Entry::getValue));
        list.forEach(System.out::println);
    }
}
Ali Katkar
  • 517
  • 4
  • 7