Shaharg
Shaharg

Reputation: 1029

How to add an element to HashMap<K, List<V>> using merge function

When maintaining a HashMap, merge is a very useful command. I like the concise way to add a number to a sum using expressions like map.merge(key, value, Double::sum);

I have a HashMap<String, List> that I want to update. I could use something like

if (!map.containsKey(key)) map.put(key, new List());
map.get(key).add(value);

is there a more elegant way using merge to achieve this?

Upvotes: 3

Views: 1856

Answers (4)

satyam singhal
satyam singhal

Reputation: 3

We can make use of the fact that the BiFunction here takes oldValue List (the current value against the key) and newValue List (the value you want to add/remove/whatever to create the new resulting value for the key) as arguments and operate on them to return a third List which will be the new state for this key in the map.

map.merge(key, Arrays.asList(new String[]{newStringToBeAdded}), (oldList, newList) -> {
                return new ArrayList<String>(){{
                    addAll(oldList);
                    addAll(newList);
                }};
            });

Here we are using Double-Brace Initialization for convenience. eg. I used it to get vertical order traversal:

static void verticalTraversalHelper(TreeNode root, int horizontalDistance, Map<Integer, List<Integer>> map) {
    if (root == null) return;
    map.merge(horizontalDistance, Arrays.asList(new Integer[]{root.val}),
            (oldList, newList) -> {
                return new ArrayList<Integer>(){{
                    addAll(oldList);
                    addAll(newList);
                }};
            });
    verticalTraversalHelper(root.left, horizontalDistance - 1, map);
    verticalTraversalHelper(root.right, horizontalDistance + 1, map);
}

collects me the nodes of the tree in map as we would hope.

Upvotes: 0

Lii
Lii

Reputation: 12112

Often when you use a Map<String, List<Something>> the code would be shorter and more elegant if you instead used a dedicated multi-map class instead of a map.

The Guava library contains such a class.

Multimap<String, Double> multimap = ArrayListMultimap.create();
...
multimap.put("someKey", 1.0);
multimap.putAll("someKey", List.of(2.0, 3.0));
System.out.println(multimap); // Prints {someKey=[1.0, 2.0, 3.0]}

Multi-map is my favourite data-structure! Check it out! It not only will make the code shorter and simpler, it also communicates the intent of the code more clearly to readers.

Upvotes: 0

hello123
hello123

Reputation: 951

If you have all of the data you need to build the Map up-front then you can do this

final Map<String, List<X>> result = list.stream().collect(Collectors.groupingBy(x -> x.key, Collectors.toList()));

Otherwise, you could use computeIfAbsent, but it's basically the same as what you've already got

result.computeIfAbsent(x.key, (key) -> new ArrayList<>()).add(x);

Upvotes: 0

M A
M A

Reputation: 72844

The merge method can be similarly used here:

map.merge(key,
     List.of(value),
     (l1, l2) -> Stream.concat(l1.stream(), Stream.of(value)).collect(Collectors.toList()));

Note: I used Stream here because the list can be immutable (this is the case with the initial value List.of(value))

Unless you're using a ConcurrentHashMap, however, I doubt this would be as elegant as the simple if solution.

Upvotes: 3

Related Questions