1. 概述
在本文中,我们将探讨 Java 中比较两个 HashMap
的不同方式。
我们会讨论多种方法来判断两个 HashMap
是否相似。此外,还会使用 Java 8 的 Stream API 和 Guava 库来获取两个 HashMap
之间的详细差异。
2. 使用 Map.equals()
方法
最简单的方式是使用 Map.equals()
方法来判断两个 HashMap
是否拥有相同的键值对:
@Test
public void whenCompareTwoHashMapsUsingEquals_thenSuccess() {
Map<String, String> asiaCapital1 = new HashMap<String, String>();
asiaCapital1.put("Japan", "Tokyo");
asiaCapital1.put("South Korea", "Seoul");
Map<String, String> asiaCapital2 = new HashMap<String, String>();
asiaCapital2.put("South Korea", "Seoul");
asiaCapital2.put("Japan", "Tokyo");
Map<String, String> asiaCapital3 = new HashMap<String, String>();
asiaCapital3.put("Japan", "Tokyo");
asiaCapital3.put("China", "Beijing");
assertTrue(asiaCapital1.equals(asiaCapital2));
assertFalse(asiaCapital1.equals(asiaCapital3));
}
✅ Map.equals()
的比较逻辑是通过 Object.equals()
对每个键和值进行比较。这意味着只有在键和值都正确实现了 equals()
方法时,比较才会生效。
⚠️ 举个反例,如果值是数组类型,Map.equals()
就会失效,因为数组的 equals()
方法默认比较的是引用,而不是内容:
@Test
public void whenCompareTwoHashMapsWithArrayValuesUsingEquals_thenFail() {
Map<String, String[]> asiaCity1 = new HashMap<String, String[]>();
asiaCity1.put("Japan", new String[] { "Tokyo", "Osaka" });
asiaCity1.put("South Korea", new String[] { "Seoul", "Busan" });
Map<String, String[]> asiaCity2 = new HashMap<String, String[]>();
asiaCity2.put("South Korea", new String[] { "Seoul", "Busan" });
asiaCity2.put("Japan", new String[] { "Tokyo", "Osaka" });
assertFalse(asiaCity1.equals(asiaCity2));
}
3. 使用 Java 8 的 Stream API
我们也可以使用 Java 8 的 Stream API 自定义比较逻辑:
private boolean areEqual(Map<String, String> first, Map<String, String> second) {
if (first.size() != second.size()) {
return false;
}
return first.entrySet().stream()
.allMatch(e -> e.getValue().equals(second.get(e.getKey())));
}
使用方式如下:
@Test
public void whenCompareTwoHashMapsUsingStreamAPI_thenSuccess() {
assertTrue(areEqual(asiaCapital1, asiaCapital2));
assertFalse(areEqual(asiaCapital1, asiaCapital3));
}
如果需要支持数组值的比较,我们可以定制一个支持数组的版本:
private boolean areEqualWithArrayValue(Map<String, String[]> first, Map<String, String[]> second) {
if (first.size() != second.size()) {
return false;
}
return first.entrySet().stream()
.allMatch(e -> Arrays.equals(e.getValue(), second.get(e.getKey())));
}
这样就可以成功比较包含数组值的 HashMap:
@Test
public void whenCompareTwoHashMapsWithArrayValuesUsingStreamAPI_thenSuccess() {
assertTrue(areEqualWithArrayValue(asiaCity1, asiaCity2));
assertFalse(areEqualWithArrayValue(asiaCity1, asiaCity3));
}
4. 比较 HashMap 的键与值
4.1. 比较键集合
可以通过比较 keySet()
来判断两个 HashMap
是否具有相同的键:
@Test
public void whenCompareTwoHashMapKeys_thenSuccess() {
assertTrue(asiaCapital1.keySet().equals(asiaCapital2.keySet()));
assertFalse(asiaCapital1.keySet().equals(asiaCapital3.keySet()));
}
4.2. 比较键对应的值
我们可以使用 Stream API 实现一个方法,来判断哪些键对应的值是相同的:
private Map<String, Boolean> areEqualKeyValues(Map<String, String> first, Map<String, String> second) {
return first.entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey(),
e -> e.getValue().equals(second.get(e.getKey()))));
}
使用示例:
@Test
public void whenCompareTwoHashMapKeyValuesUsingStreamAPI_thenSuccess() {
Map<String, String> asiaCapital3 = new HashMap<String, String>();
asiaCapital3.put("Japan", "Tokyo");
asiaCapital3.put("South Korea", "Seoul");
asiaCapital3.put("China", "Beijing");
Map<String, String> asiaCapital4 = new HashMap<String, String>();
asiaCapital4.put("South Korea", "Seoul");
asiaCapital4.put("Japan", "Osaka");
asiaCapital4.put("China", "Beijing");
Map<String, Boolean> result = areEqualKeyValues(asiaCapital3, asiaCapital4);
assertEquals(3, result.size());
assertThat(result, hasEntry("Japan", false));
assertThat(result, hasEntry("South Korea", true));
assertThat(result, hasEntry("China", true));
}
5. 使用 Guava 获取 Map 差异
Guava 提供了强大的 Maps.difference()
方法,用于获取两个 Map 的详细差异。
5.1. entriesDiffering()
:获取键相同但值不同的条目
@Test
public void givenDifferentMaps_whenGetDiffUsingGuava_thenSuccess() {
Map<String, String> asia1 = new HashMap<String, String>();
asia1.put("Japan", "Tokyo");
asia1.put("South Korea", "Seoul");
asia1.put("India", "New Delhi");
Map<String, String> asia2 = new HashMap<String, String>();
asia2.put("Japan", "Tokyo");
asia2.put("China", "Beijing");
asia2.put("India", "Delhi");
MapDifference<String, String> diff = Maps.difference(asia1, asia2);
Map<String, ValueDifference<String>> entriesDiffering = diff.entriesDiffering();
assertFalse(diff.areEqual());
assertEquals(1, entriesDiffering.size());
assertThat(entriesDiffering, hasKey("India"));
assertEquals("New Delhi", entriesDiffering.get("India").leftValue());
assertEquals("Delhi", entriesDiffering.get("India").rightValue());
}
5.2. entriesOnlyOnRight()
和 entriesOnlyOnLeft()
:获取仅存在于一边的条目
@Test
public void givenDifferentMaps_whenGetEntriesOnOneSideUsingGuava_thenSuccess() {
MapDifference<String, String> diff = Maps.difference(asia1, asia2);
Map<String, String> entriesOnlyOnRight = diff.entriesOnlyOnRight();
Map<String, String> entriesOnlyOnLeft = diff.entriesOnlyOnLeft();
assertEquals(1, entriesOnlyOnRight.size());
assertEquals(1, entriesOnlyOnLeft.size());
assertThat(entriesOnlyOnRight, hasEntry("China", "Beijing"));
assertThat(entriesOnlyOnLeft, hasEntry("South Korea", "Seoul"));
}
5.3. entriesInCommon()
:获取完全一致的条目
@Test
public void givenDifferentMaps_whenGetCommonEntriesUsingGuava_thenSuccess() {
MapDifference<String, String> diff = Maps.difference(asia1, asia2);
Map<String, String> entriesInCommon = diff.entriesInCommon();
assertEquals(1, entriesInCommon.size());
assertThat(entriesInCommon, hasEntry("Japan", "Tokyo"));
}
5.4. 自定义 Maps.difference()
的比较行为
默认情况下,Maps.difference()
使用 equals()
和 hashCode()
比较对象。对于未正确实现这两个方法的对象(如数组),我们可以使用 Equivalence
来自定义比较逻辑:
@Test
public void givenSimilarMapsWithArrayValue_whenCompareUsingGuavaEquivalence_thenSuccess() {
Equivalence<String[]> eq = new Equivalence<String[]>() {
@Override
protected boolean doEquivalent(String[] a, String[] b) {
return Arrays.equals(a, b);
}
@Override
protected int doHash(String[] value) {
return value.hashCode();
}
};
MapDifference<String, String[]> diff = Maps.difference(asiaCity1, asiaCity2, eq);
assertTrue(diff.areEqual());
diff = Maps.difference(asiaCity1, asiaCity3, eq);
assertFalse(diff.areEqual());
}
6. 总结
本文介绍了多种比较 Java 中 HashMap
的方式,包括使用 equals()
、Stream API 和 Guava 的 Maps.difference()
。这些方法各有适用场景,可以根据具体需求选择合适的方式。
源码可以在 GitHub 上找到。