Reputation: 1029
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
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
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
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
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