20

Java 9 offers Map.of() feature to easily create a map with fixed values.

Problem: I want to create a map that preserves order of insertion like LinkedHashMap. Is that possible with that factory? At least map.of() does not preserv the order...

Naman
  • 27,789
  • 26
  • 218
  • 353
membersound
  • 81,582
  • 193
  • 585
  • 1,120

5 Answers5

18

There isn't a factory method like LinkedHashMap::of indeed, and a Map does not have an order per-se, so the only way I see it is to build a LinkedHashMap if you really needed one.

Btw from the JEP itself:

Static factory methods on concrete collection classes (e.g., ArrayList, HashSet) have been removed from this proposal ...

There is another wrinkle, which is that static methods on classes are inherited by subclasses. Suppose a static factory method HashMap.of() were to be added. Since LinkedHashMap is a subclass of HashMap, it would be possible for application code to call LinkedHashMap.of(). This would end up calling HashMap.of(), not at all what one would expect!

Point here is that static methods are inherited, but not overridable, thus if such a method would have been added to HashMap it could have not been overridden in LinkedHashMap.

If you can use guava, you could use ImmutableMap that is documented as:

An immutable, hash-based Map with reliable user-specified iteration order...

Community
  • 1
  • 1
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • On the last edit, I've noticed this in quite a few places and would want to learn if `LinkedHashMap` and Guava's `ImmutableMap` has any comparative analysis that I can go through? I couldn't though sense anything other than immutability. – Naman Sep 10 '18 at 09:38
  • @nullpointer I am no aware of such analysis, I seem to trust guava quite a lot :| what I like is that it is build using a one-liner, as opposed `Collections::unmodifiableMap` that would wrap a `LinkedHashMap`... – Eugene Sep 10 '18 at 09:49
  • My upvote for ImmutableMap. That's the best solution IMO. – ZhekaKozlov Sep 10 '18 at 09:57
  • @ZhekaKozlov I wish I could return this upvote for the proposed names for `BiCollector`... – Eugene Sep 10 '18 at 09:58
  • Actually, `static` methods are overridable, i.e. if `LinkedHashMap` provided such factory methods with the same signature as in `HashMap`, they would override them, so `LinkedHashMap.of(…)` would end up at the right method, *if* `LinkedHashMap` provides such a method. You could assume that this would be the case when both classes stem from the same JRE, but what about a `ThirdPartyMap` inheriting from either of these classes. The worst thing would be, if it gets adapted to provide factory methods *once*, to end up with a mixture of overridden and non-overridden methods in a future JRE version… – Holger Sep 12 '18 at 10:57
  • @Holger to be pedantic, they can be hidden [(8.4.8.2. Hiding (by Class Methods))](https://docs.oracle.com/javase/specs/jls/se10/html/jls-8.html#jls-8.4.8.2), not overridden [8.4.8.1. Overriding (by Instance Methods))](https://docs.oracle.com/javase/specs/jls/se10/html/jls-8.html#jls-8.4.8.1), but your point still holds - it could have been done, but it would have incurred a significant implementation- and maintainance cost. – Hulk Sep 12 '18 at 12:31
  • @Hulk indeed, and even `javac` fails to use the correct terminology, e.g. in its error messages. That one time I didn’t consult the specification, it immediately bites me… – Holger Sep 12 '18 at 12:39
  • @Holger I love those btw! :) when you have a `final static method` in Parent and the same signature in Child (without `final`) - `javac` will say something like overridable method is final – Eugene Sep 12 '18 at 12:41
5

As documented on the Java apidoc of Map (emphasis mine):

Unmodifiable Maps

The Map.of, Map.ofEntries, and Map.copyOf static factory methods provide a convenient way to create unmodifiable maps. The Map instances created by these methods have the following characteristics:

  • ...
  • The iteration order of mappings is unspecified and is subject to change.
  • ...

Unfortunately, there is no equivalent convenience method in the Java API that creates a LinkedHashMap. If you want a consistent iteration order, then you will need to manually create a LinkedHashMap and populate it (and - if needed - wrap it using Collections.unmodifiableMap).

Consider creating your own convenience method that does the equivalent of Map.of but with a consistent iteration order (or find an existing library that already provides this).

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
0

You can also use vavr.io in the following way:

Map<String, String> mapPreservingInsertionOrder = io.vavr.collection.LinkedHashMap.of("key1", "val1", "key2", "val2").toJavaMap();
miro
  • 131
  • 2
  • 9
0

Because there seems to be no method that is doing what you want in the JDK you have to implement it by yourself or use a existing library. Creating such helper method isn't that hard:

public static <K, V> LinkedHashMap<K, V> of(Collection<Entry<? extends K, ? extends V>> entries) {
  final LinkedHashMap<K, V> map = new LinkedHashMap<>();
  entries.forEach(entry -> map.put(entry.getKey(), entry.getValue()));
  return map;
}

And then you could use it like this:

of(List.of(Map.entry("Hello", "World"), Map.entry("Goodnight", "Moon")));

This solution has the advantage over the Map.of in the JDK that the key value pairs are provided as entry pairs and not just as lose pairs by parameter index.

Markus Hettich
  • 544
  • 6
  • 12
0

If you don't care about the order, but only need it to be consistent, you can use a sorted map like TreeMap in conjunction with Map.of

new TreeMap<>(Map.of("key1", "value1", "key0", "value0", "key3", "value3"))

This should always return a consistent map with the keys sorted.

Note: If you need it to be Unmodifiable, you'll need to wrap it with something like

Collections.unmodifiableMap(<<<TreeMap Here>>>)
ScrappyDev
  • 2,307
  • 8
  • 40
  • 60