Java Ordered Map

Java Ordered Map

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.

Java Class that implements Map and keeps insertion order?

I suggest a LinkedHashMap or a TreeMap. A LinkedHashMap keeps the keys in the order they were inserted, while a TreeMap is kept sorted via a Comparator or the natural Comparable ordering of the keys.

Since it doesn't have to keep the elements sorted, LinkedHashMap should be faster for most cases; TreeMap has O(log n) performance for containsKey, get, put, and remove, according to the Javadocs, while LinkedHashMap is O(1) for each.

If your API that only expects a predictable sort order, as opposed to a specific sort order, consider using the interfaces these two classes implement, NavigableMap or SortedMap. This will allow you not to leak specific implementations into your API and switch to either of those specific classes or a completely different implementation at will afterwards.

How to preserve insertion order in HashMap?

LinkedHashMap is precisely what you're looking for.

It is exactly like HashMap, except that when you iterate over it, it presents the items in the insertion order.

Ordered List Map implementation in Java

If you need your items in a specific order, LinkedHashMap is your friend - it keeps items in insertion order. TreeMap will keep your items in an order defined by either a Comparator you give or a compareTo method of the key.

Converting Java map into ordered collection based on map values

Use a LinkedHashMap instead of a Map. The difference is that the first one has an order, so when you sort it it will remain sorted (until you modify or add new elements)

By the way, you can stream over a Map

answerCounts.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.forEach(System.out::println);

This will sort the Map but in the wrong order, and i can't find a way to reverse it

Ordering MapString, Integer by ListString using streams

The HashMap cannot be used to store orderedData because it does not guarantee ordering of its keys, so LinkedHashMap should be used which maintains insertion order.

Ordering by the data presented in orderSequence using streams may be implemented in two modes:

  1. Keep only the values available in nonOrderedData:
Map<String, Integer> nonOrderedData = Map.of(
"b", 1, "e", 4, "a", 0, "o", 5, "d", 7
);
List<String> orderSequence = Arrays.asList(
"a", "e", "i", "o", "u", "b", "c", "d"
);

Map<String, Integer> reordered = orderSequence
.stream()
.filter(nonOrderedData::containsKey)
.collect(Collectors.toMap(
key -> key, nonOrderedData::get,
(v1, v2) -> v1, LinkedHashMap::new
));

System.out.println(reordered);

Output:

{a=0, e=4, o=5, b=1, d=7}

It is similar to INNER JOIN between orderSequence and nonOrderedData.


  1. Filling reorderedData with some default value if the key from orderSequence is missing in nonOrderedData:
Map<String, Integer> reorderedWithDefault = orderSequence
.stream()
.collect(Collectors.toMap(
key -> key, nonOrderedData.getOrDefault(key, -1),
(v1, v2) -> v1, LinkedHashMap::new
));

System.out.println(reorderedWithDefault);

Output:

{a=0, e=4, i=-1, o=5, u=-1, b=1, c=-1, d=7}

It is similar to LEFT JOIN between orderSequence and nonOrderedData.


Update

In the above-mentioned implementations the key-value pairs in nonOrderedData that do not match to the keys in orderSequence are skipped altogether. Such keys may be tracked (and added later to the reordered result) using Map::remove (Object key) which returns a value of the key being removed.

However, the following two code examples modify the state of nonOrderedData outside the stream execution.

  1. Keep the keys and related values only from nonOrderedData, place the non-matched pairs to the end:
Map<String, Integer> nonOrderedData = new HashMap<>(Map.of(
"b", 1, "e", 4, "z", 8, "a", 0, "q", 6,
"f", 5, "d", 7
));
List<String> orderSequence = Arrays.asList("a", "e", "i", "o", "u", "b", "c", "d");

Map<String, Integer> reordered = orderSequence
.stream()
.filter(nonOrderedData::containsKey)
.collect(Collectors.toMap(
key -> key, nonOrderedData::remove,
(v1, v2) -> v1, LinkedHashMap::new
));

SortedMap<String, Integer> remainder = new TreeMap<>(nonOrderedData);
System.out.println("remained: " + remainder);

reordered.putAll(remainder);

System.out.println(reordered);

Output:

remained: {f=5, q=6, z=8}
{a=0, e=4, b=1, d=7, f=5, q=6, z=8}

It is similar to RIGHT JOIN between orderSequence and nonOrderedData.


  1. Keep all values from both orderSequence and nonOrderedData similar to FULL JOIN

Here default values will be provided for the non-mapped keys in orderSequence and non-matched keys from nonOrderedData will be added to the end.

Map<String, Integer> reorderedFull = orderSequence
.stream()
.peek(key -> nonOrderedData.computeIfAbsent(key, (k) -> -1)) // default value
.collect(Collectors.toMap(
key -> key, nonOrderedData::remove,
(v1, v2) -> v1, LinkedHashMap::new
));

SortedMap<String, Integer> remainderFull = new TreeMap<>(nonOrderedData);
System.out.println("remained: " + remainderFull);

reorderedFull.putAll(remainderFull);
System.out.println(reorderedFull);

Output:

remained: {f=5, q=6, z=8}
{a=0, e=4, i=-1, o=-1, u=-1, b=1, c=-1, d=7, f=5, q=6, z=8}


Related Topics



Leave a reply



Submit