3

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);
}
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Hector
  • 4,016
  • 21
  • 112
  • 211
  • 1
    extract your own methods to describe what you expected. e.g: `mapOf(with("foo","bar"), ... ,with("key","value"));` – holi-java Aug 04 '17 at 13:45
  • @AndrewTobilko I am looking for a "self Documenting" solution, and I always like the Builder Pattern. I could then add in De Duplication. One constraint is that I would prefer standard Java solution. – Hector Aug 04 '17 at 14:07
  • use a `static {}` block? it's not single-line, but much shorter by character count – the8472 Aug 04 '17 at 14:16

1 Answers1

5

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.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
  • nice work, I would guess you meant entries.stream().collect(toMap... to be entries.stream().collect(Collectors.toMap... ? – Hector Aug 04 '17 at 14:25