MapUtils { @SuppressWarnings("unchecked") public static <K, V> void deepMerge(Map<K, V> m1, Map<? extends K, ? extends V> m2) { for (K k : m2.keySet()) { V v1 = m1.get(k); V v2 = m2.get(k); if (v1 instanceof Collection && v2 instanceof Collection) { if (((Collection<Object>)v1).containsAll((Collection<Object>)v2)) { } else { ((Collection<Object>)v1).addAll((Collection<Object>)v2); } } else if (v1 instanceof Map && v2 instanceof Map) { deepMerge((Map<Object, Object>)v1, (Map<Object, Object>)v2); } else { V existingV = m1.get(k); if (existingV != null && existingV.equals(v2)) { } else { m1.put(k, v2); } } } }  @SuppressWarnings("unchecked") static void deepMerge(Map<K, V> m1, Map<? extends K, ? extends V> m2);  }
@Test public void empty() { Map<String, String> m1 = new HashMap<>(); Map<String, String> m2 = new HashMap<>(); m2.put("a", "1"); m2.put("b", "2"); MapUtils.deepMerge(m1, m2); assertEquals(m2, m1); }
@Test public void existingKey() { Map<String, String> m1 = new HashMap<>(); m1.put("a", "1"); m1.put("b", "0"); Map<String, String> m2 = new HashMap<>(); m2.put("b", "2"); m2.put("c", "3"); Map<String, String> expected = new HashMap<>(); expected.put("a", "1"); expected.put("b", "2"); expected.put("c", "3"); MapUtils.deepMerge(m1, m2); assertEquals(expected, m1); }
@Test public void nestedMap() { Map<String, String> nested1 = new HashMap<>(); nested1.put("a", "1"); nested1.put("b", "0"); Map<String, Object> m1 = new HashMap<>(); m1.put("nested", nested1); Map<String, String> nested2 = new HashMap<>(); nested2.put("b", "2"); nested2.put("c", "3"); Map<String, Object> m2 = new HashMap<>(); m2.put("nested", nested2); Map<String, String> nestedExpected = new HashMap<>(); nestedExpected.put("a", "1"); nestedExpected.put("b", "2"); nestedExpected.put("c", "3"); Map<String, Object> expected = new HashMap<>(); expected.put("nested", nestedExpected); MapUtils.deepMerge(m1, m2); assertEquals(expected, m1); }
@Test public void withList() { Map<String, Object> nested1 = new HashMap<>(); nested1.put("a", "1"); nested1.put("b", "0"); nested1.put("d", new ArrayList<String>(Arrays.asList("1"))); Map<String, Object> m1 = new HashMap<>(); m1.put("nested", nested1); Map<String, Object> nested2 = new HashMap<>(); nested2.put("b", "2"); nested2.put("c", "3"); nested2.put("d", Arrays.asList("2")); Map<String, Object> m2 = new HashMap<>(); m2.put("nested", nested2); Map<String, Object> nestedExpected = new HashMap<>(); nestedExpected.put("a", "1"); nestedExpected.put("b", "2"); nestedExpected.put("c", "3"); nestedExpected.put("d", Arrays.asList("1", "2")); Map<String, Object> expected = new HashMap<>(); expected.put("nested", nestedExpected); MapUtils.deepMerge(m1, m2); assertEquals(expected, m1); }
@Test public void mergeUnmodifiableMaps() { Map<String, String> m1 = new HashMap<>(); m1.put("a", "1"); m1.put("b", "0"); m1 = Collections.unmodifiableMap(m1); Map<String, String> m2 = new HashMap<>(); m2.put("a", "1"); m2.put("b", "0"); m2 = Collections.unmodifiableMap(m2); Map<String, String> expected = new HashMap<>(); expected.put("a", "1"); expected.put("b", "0"); MapUtils.deepMerge(m1, m2); assertEquals(expected, m1); }
@Test(expected = UnsupportedOperationException.class) public void mergeUnmodifiableMapsDifferent() { Map<String, String> m1 = new HashMap<>(); m1.put("a", "1"); m1.put("b", "0"); m1 = Collections.unmodifiableMap(m1); Map<String, String> m2 = new HashMap<>(); m2.put("a", "1"); m2.put("b", "2"); m2 = Collections.unmodifiableMap(m2); MapUtils.deepMerge(m1, m2); }
@Test public void mergeUnmodifiableCollections() { Map<String, Object> m1 = new HashMap<>(); m1.put("a", "1"); m1.put("b", Collections.unmodifiableList(Arrays.asList("1", "2"))); Map<String, Object> m2 = new HashMap<>(); m2.put("a", "1"); m2.put("b", Collections.unmodifiableList(Arrays.asList("1", "2"))); Map<String, Object> expected = new HashMap<>(); expected.put("a", "1"); expected.put("b", Arrays.asList("1", "2")); MapUtils.deepMerge(m1, m2); assertEquals(expected, m1); }