How to putAll on Java hashMap contents of one to another, but not replace existing keys and values?

185,171

Solution 1

It looks like you are willing to create a temporary Map, so I'd do it like this:

Map tmp = new HashMap(patch);
tmp.keySet().removeAll(target.keySet());
target.putAll(tmp);

Here, patch is the map that you are adding to the target map.

Thanks to Louis Wasserman, here's a version that takes advantage of the new methods in Java 8:

patch.forEach(target::putIfAbsent);

Solution 2

Using Guava's Maps class' utility methods to compute the difference of 2 maps you can do it in a single line, with a method signature which makes it more clear what you are trying to accomplish:

public static void main(final String[] args) {
    // Create some maps
    final Map<Integer, String> map1 = new HashMap<Integer, String>();
    map1.put(1, "Hello");
    map1.put(2, "There");
    final Map<Integer, String> map2 = new HashMap<Integer, String>();
    map2.put(2, "There");
    map2.put(3, "is");
    map2.put(4, "a");
    map2.put(5, "bird");

    // Add everything in map1 not in map2 to map2
    map2.putAll(Maps.difference(map1, map2).entriesOnlyOnLeft());
}

Solution 3

Just iterate and add:

for(Map.Entry e : a.entrySet())
  if(!b.containsKey(e.getKey())
    b.put(e.getKey(), e.getValue());

Edit to add:

If you can make changes to a, you can also do:

a.putAll(b)

and a will have exactly what you need. (all the entries in b and all the entries in a that aren't in b)

Solution 4

You can make it in just 1 line if you change maps order in @erickson's solution:

mapWithNotSoImportantValues.putAll( mapWithImportantValues );

In this case you replace values in mapWithNotSoImportantValues with value from mapWithImportantValues with the same keys.

Solution 5

Java 8 solution using Map#merge

As of you can use Map#merge(K key, V value, BiFunction remappingFunction) which merges a value into the Map using remappingFunction in case the key is already found in the Map you want to put the pair into.

// using lambda
newMap.forEach((key, value) -> map.merge(key, value, (oldValue, newValue) -> oldValue));
// using for-loop
for (Map.Entry<Integer, String> entry: newMap.entrySet()) {
    map.merge(entry.getKey(), entry.getValue(), (oldValue, newValue) -> oldValue);
}

The code iterates the newMap entries (key and value) and each one is merged into map through the method merge. The remappingFunction is triggered in case of duplicated key and in that case it says that the former (original) oldValue value will be used and not rewritten.

With this solution, you don't need a temporary Map.


Let's have an example of merging newMap entries into map and keeping the original values in case of the duplicated antry.

Map<Integer, String> newMap = new HashMap<>();
newMap.put(2, "EVIL VALUE");                         // this will NOT be merged into
newMap.put(4, "four");                               // this WILL be merged into
newMap.put(5, "five");                               // this WILL be merged into

Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");

newMap.forEach((k, v) -> map.merge(k, v, (oldValue, newValue) -> oldValue));

map.forEach((k, v) -> System.out.println(k + " " + v));
1 one
2 two
3 three
4 four
5 five
Share:
185,171
rapadura
Author by

rapadura

love gnu/linux and free-software

Updated on January 25, 2021

Comments

  • rapadura
    rapadura over 3 years

    I need to copy all keys and values from one A HashMap onto another one B, but not to replace existing keys and values.

    Whats the best way to do that?

    I was thinking instead iterating the keySet and checkig if it exist or not, I would

    Map temp = new HashMap(); // generic later
    temp.putAll(Amap);
    A.clear();
    A.putAll(Bmap);
    A.putAll(temp);
    
  • Fabian Barney
    Fabian Barney almost 13 years
    1.) entrySet() returns a Set<Map.Entry>. 2.) There is no method contains(...)for java.util.Map.
  • rapadura
    rapadura almost 13 years
    Isnt there a smarter way? Like a Collections utility or so? I hate all the for loops everywhere.
  • Reverend Gonzo
    Reverend Gonzo almost 13 years
    @AntonioP: All the for loops everywhere? There's one for loop and it's about as straightforward as you can get.
  • Fabian Barney
    Fabian Barney almost 13 years
    +1 then, just works when Maps used not with raw type, I think. Got myself a bit fuddled with the Map.Entry :-)
  • rapadura
    rapadura almost 13 years
    @Reverend Gonzo, ah, elsewhere in the code. :) Wont .putAll replace existing keys and values of a? Ill go with this sense it does seem the most straightforward way.
  • Reverend Gonzo
    Reverend Gonzo almost 13 years
    In this case you don't need to do removeAll since they'll get overriden anyways.
  • Steve Kuo
    Steve Kuo almost 13 years
    @AntonioP, if you want to do something custom you can't avoid custom code (like looping).
  • brady
    brady almost 13 years
    @Reverend Gonzo: Overwritten by what?
  • brady
    brady almost 10 years
    @luksmir No, that would remove the existing entries that should be preserved.
  • betomoretti
    betomoretti over 8 years
    Hi! Why mapWithValues.putAll(mapWithAnotherValues) doens't work?
  • brady
    brady over 8 years
    @betomoretti Because if both maps have a value for a key, that would replace the original value with another. That would violate the requirement "not to replace existing keys and values."
  • betomoretti
    betomoretti over 8 years
    @erickson I messed up the requirement. But , in case of that requirement doesn't exists, it's work? Sorry for my English and thanks!
  • betomoretti
    betomoretti over 8 years
    @erickson Well, mapWithValues.putAll(mapWithAnotherValues) void mapWithValues. So, this it's not a good solution. In fact, doesn't work.
  • Louis Wasserman
    Louis Wasserman over 8 years
    @erickson a much more compact Java 8 version: patch.forEach(target::putIfAbsent).
  • Simon Baars
    Simon Baars over 6 years
    I guess this is better for performance than to create a whole new map?
  • user1735921
    user1735921 almost 6 years
    can we have putAllIfAbsent ? thats what I need ?