0

I need to merge two maps in the first one by the following rules:

I need to remove all of the keys from map1 which are not present in the map2. I need to update all keys in the map1 with the appropriate values which are present in map2 under these keys.

This is my current code:

Set<String> keysToRemove = new HashSet<>();
map1.forEach((k, v) -> {
  if (!map2.containsKey(k)) {
    keysToRemove.add(k);
  } else {
    map1.put(k, map2.get(k));
  }
});

for (String k : keysToRemove) {
  map1.remove(k);
}

I'm not sure my code is optimal and can be improved. Could you please show the way how to implement this task more effectively?

alexanoid
  • 24,051
  • 54
  • 210
  • 410
  • 5
    Based on your requirements, it seems like `map1` needs to be made into a copy of `map2`. Is that what you intend? – rgettman May 03 '18 at 17:35
  • yeah, i'd just create a copy of map2 and set map1 to that copy – RAZ_Muh_Taz May 03 '18 at 17:36
  • I'm trying to solve the following issue - https://stackoverflow.com/questions/50149323/spring-data-neo4j-5-update-dynamic-properties so I'm trying to keep the same referense on the original map1 and do not substitute it with the new map. – alexanoid May 03 '18 at 17:39
  • 2
    So just `clear()` and `putAll(map2)`. – shmosel May 03 '18 at 17:59

3 Answers3

2

You can achieve it in two lines

This solution is based on the comment (which gave an impression as the OP wanted map1 to be an exact copy of map2)

[...]I'm trying to keep the same referense on the original map1 and do not substitute it with the new map.[sic]

//Retains only those keys that are in map2
map1.keySet().retainAll(map2.keySet()); 

//(Possibly) Overwrite value for each key in map2 into map1
map2.forEach(map1::put);

I don't believe it would help you improve the performance though.

EDIT: As suggested by Jacob G.@ you can have map1.putAll(map2) for the last line

EDIT2:

If we consider the OP (and not the comments), if there are any keys in map2 that is not there in map1, it should not end up in map1 and hence the last statement becomes

map1.forEach((key, value) -> map1.put(key, map2.get(key)));
Thiyagu
  • 17,362
  • 5
  • 42
  • 79
  • I'd change the second line to `map1.putAll(map2)` – Jacob G. May 03 '18 at 18:07
  • @shmosel Ah yes.. Suddenly you seem to get all possible combinations :) – Thiyagu May 03 '18 at 18:15
  • This approach introduce two loops. It's still not optimal – Mạnh Quyết Nguyễn May 03 '18 at 18:21
  • @MạnhQuyếtNguyễn I never said it's more optimal. – Thiyagu May 03 '18 at 18:25
  • I suppose, the goal is not to overwrite existing values, so the second statement should be `map2.forEach(map1::putIfAbsent)`. Otherwise, the result would not be different to `map1.clear(); map1.putAll(map2);`… – Holger May 04 '18 at 11:34
  • @Holger From the OP, `map1.put(k, map2.get(k));` is executed for all keys in map2. What you have said latter is exactly correct :). The OP just wants the clone of map2 but in the same object reference pointed to be map1. – Thiyagu May 04 '18 at 12:04
  • @Holger Refer to shmosel's comment above (map1.clear()) – Thiyagu May 04 '18 at 12:04
  • Yes, I already noticed that `map1.clear(); map1.putAll(map2);` seems to be what the OP actually wants, though there’s still a behavioral difference to the question and accepted answer regarding keys present in `map2` but not in `map1`. – Holger May 04 '18 at 12:19
  • @Holger I'm not sure how the accepted answer is different as it *always* overwrites if map2 contains a key (else it removes it from map1) – Thiyagu May 04 '18 at 13:31
  • @Holger I misunderstood your interpretation which is correct. From the OP post, if there are extra keys in map2, it shouldn't end up in map1. – Thiyagu May 04 '18 at 15:07
  • @Holger But still `map2.forEach(map1::putIfAbsent)` is incorrect right? Please see my EDIT – Thiyagu May 04 '18 at 15:30
  • 1
    @user7 indeed, if you want to do the same as the OP’s code, you’d rather need `map2.forEach(map1::replace)`. But it’s not clear whether this is what the OP really wants, so `map1.clear(); map1.putAll(map2);` might still be the right thing, actually. – Holger May 04 '18 at 16:42
0

I think you can remove the second loop by using Iterator

Iterator<Map.Entry<K,V>> iter = map1.entrySet().iterator();
while (iter.hasNext()) {
  Map.Entry<K,V> entry = iter.next();
  if(not map2 contain k){
    iter.remove();
  } else {
    entry.put new data
  }
}

The key here is, you can't update the map while loop over Map.entrySet(), it will raise ConcurrentModificationException, but you can do it with Iterator.

Mạnh Quyết Nguyễn
  • 17,677
  • 1
  • 23
  • 51
0

Another approach could be filtering only with the keys available in map2, and finally using map to replace existing values with the ones on map2. Something similar to this might do the trick:

map1.entrySet().stream().
filter(e -> map2.containsKey(e.getKey())).
map(e -> map2.get(e.getKey()))