在Java中比较两个HashMaps的情况

评论 0 浏览 0 2018-12-12

1.概述

在本教程中,我们将探索在Java中比较两个HashMaps的不同方法

我们将讨论多种方法来检查两个HashMaps是否相似。我们还将使用Java 8 Stream API和Guava来获得不同HashMaps之间的详细差异。

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));
}

在这里,我们正在创建三个HashMap对象并添加条目。然后我们使用Map.equals()来检查两个HashMap是否有相同的条目。

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 Stream API

我们也可以使用Java 8 Stream API来实现我们自己的方法来比较HashMap

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())));
}

为了简单起见,我们实现了areEqual()方法,现在我们可以用它来比较HashMap<String, String>对象。

@Test
public void whenCompareTwoHashMapsUsingStreamAPI_thenSuccess() {
    assertTrue(areEqual(asiaCapital1, asiaCapital2));
    assertFalse(areEqual(asiaCapital1, asiaCapital3));
}

但我们也可以定制自己的方法areEqualWithArrayValue(),通过使用Arrays.equals()来处理数组的值,以比较两个数组。

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())));
}

Map.equals()不同,我们自己的方法将成功地将HashMaps与数组值进行比较。

@Test
public void whenCompareTwoHashMapsWithArrayValuesUsingStreamAPI_thenSuccess() {
    assertTrue(areEqualWithArrayValue(asiaCity1, asiaCity2)); 
    assertFalse(areEqualWithArrayValue(asiaCity1, asiaCity3));
}

4.比较HashMap键和值

接下来,让我们看看如何比较两个HashMap键和它们相应的值。

4.1.比较HashMap

首先,我们可以通过比较两个HashMapsKeySet()来检查两个HashMaps是否有相同的键。

@Test
public void whenCompareTwoHashMapKeys_thenSuccess() {
    assertTrue(asiaCapital1.keySet().equals(asiaCapital2.keySet())); 
    assertFalse(asiaCapital1.keySet().equals(asiaCapital3.keySet()));
}

4.2.比较HashMap

接下来,我们将看到如何逐一比较HashMap的值。

我们将使用Stream API实现一个简单的方法来检查哪些键在两个HashMaps中的值是相同的。

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()))));
}

我们现在可以使用areEqualKeyValues()来比较两个不同的HashMaps,以详细了解哪些键具有相同的值,哪些键具有不同的值。

@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()获得两个HashMaps之间的详细差异。

该方法返回一个MapDifference对象,该对象有许多有用的方法来分析地图之间的差异。让我们看看其中的一些方法。

5.1. MapDifference.entriesDiffering()

首先,我们将使用MapDifference.entryDiffering()获得在每个HashMap中具有不同值的普通键。

@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());
}

entriesDiffering()方法返回一个新的Map,其中包含公共键的集合和ValueDifference对象作为值的集合。

每个ValueDifference对象都有一个leftValue()rightValue()方法,分别返回两个Map中的值。

5.2.MapDifference.entriesOnlyOnRight()MapDifference.entriesOnlyOnLeft()

然后,我们可以使用MapDifference. entriesOnlyOnRight()MapDifference. entriesOnlyOnLeft()来获取只存在于一个HashMap中的条目:

@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. MapDifference.entriesInCommon()

接下来,我们将使用MapDifference. 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()来比较条目,它不会对没有正确实现它们的Objects起作用。

@Test
public void givenSimilarMapsWithArrayValue_whenCompareUsingGuava_thenFail() {
    MapDifference<String, String[]> diff = Maps.difference(asiaCity1, asiaCity2);
    assertFalse(diff.areEqual());
}

但是,我们可以使用Equivalence来定制比较中使用的方法。

例如,我们将为类型String[]定义Equivalence ,以比较我们的HashMap中的String[]值,因为我们喜欢。

@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中比较HashMaps的不同方法。我们学习了多种方法来检查两个HashMaps是否相等以及如何获得详细的差异。

完整的源代码可在GitHub上获得。

最后更新2023-01-22
0 个评论
标签