I am looking for a "CLEAN & Simple" method of initialising a ConcurrentHashMap.
With Java 8 I have this:-
private static final Map<String, String> myStreamedMap = Stream.of(
new AbstractMap.SimpleImmutableEntry<>("Key1", "Value1"),
new AbstractMap.SimpleImmutableEntry<>("Key2", "Value2"),
new AbstractMap.SimpleImmutableEntry<>("Key3", "Value3"),
new AbstractMap.SimpleImmutableEntry<>("Key4", "Value4")).
collect(Collectors.toMap((entry) -> entry.getKey(), (entry) -> entry.getValue()));
Which provides the desired end result, however I feel
"new AbstractMap.SimpleImmutableEntry<>
"
Makes it hard to see whats going on.
Is there any way I can "hide" this and keep to a single line?
UPDATE
Came up with this (Obvious) solution
private static final Map<String, String> myStreamedMapWith = Stream.of(
with("Key1", "Value1"),
with("Key2", "Value2"),
with("Key3", "Value3"),
with("Key4", "Value4")).
collect(Collectors.toMap((entry) -> entry.getKey(), (entry) -> entry.getValue()));
private static AbstractMap.SimpleImmutableEntry<String, String> with(final String key, final String value) {
return new AbstractMap.SimpleImmutableEntry<>(key, value);
}
Until Java 9 gets released, there is no a convenient built-in map initialization method, so I would recommend looking at a third-party library (like Google's Guava):
new ConcurrentHashMap<>(com.google.common.collect.ImmutableMap.of("Key1", "Value1"));
Anyway, the main problem here is that you are creating an instance of the HashMap
.
from the Collectors.toMap
sources:
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
If you don't want to use any external libraries, a good way to go is using the Builder pattern. Here is a simple example:
class MapBuilder<K, V> {
private List<Map.Entry<K, V>> entries = new ArrayList<>();
public MapBuilder<K, V> with(K key, V value) {
entries.add(new AbstractMap.SimpleImmutableEntry<>(key, value));
return this;
}
public Map<K, V> build(Supplier<Map<K, V>> mapSupplier) {
return entries.stream().collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(k1, k2) -> k1,
mapSupplier
)
);
}
}
And its demonstration:
new MapBuilder().with("Key1", "Value1")
.with("Key2", "Value2")
.build(ConcurrentHashMap::new);
To be fully independent of implementation (the AbstractMap.SimpleImmutableEntry
), I suggest introducing a constructor that takes a BiFunction<KEY, VALUE, Map.Entry<KEY, VALUE>>
entry initialiser as an argument:
class MapBuilder<K, V> {
private List<Map.Entry<K, V>> entries;
private BiFunction<K, V, Map.Entry<K, V>> function;
public MapBuilder() {
entries = new ArrayList<>();
}
public MapBuilder(BiFunction<K, V, Map.Entry<K, V>> function) {
this();
this.function = function;
}
public MapBuilder<K, V> with(K key, V value) {
entries.add(function.apply(key, value));
return this;
}
public Map<K, V> build(Supplier<Map<K, V>> mapSupplier) { ... }
}
The call is gonna be changed to:
new MapBuilder<>(AbstractMap.SimpleImmutableEntry::new);
At this point, we can decide what implementation should be chosen for both an entry and a map separately.