Comparing Two HashMaps in Java – 在Java中比较两个HashMaps

最后修改: 2018年 12月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’re going to explore different ways to compare two HashMaps in Java.

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

We’ll discuss multiple ways to check if two HashMaps are similar. We’ll also use Java 8 Stream API and Guava to get the detailed differences between different HashMaps.

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

2. Using Map.equals()

2.使用Map.equals()

First, we’ll use Map.equals() to check if two HashMaps have the same entries:

首先,我们将使用Map.equals()来检查两个HashMaps是否拥有相同的条目。

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

Here, we’re creating three HashMap objects and adding entries. Then we’re using Map.equals() to check if two HashMaps have the same entries.

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

The way that Map.equals() works is by comparing keys and values using the Object.equals() method. This means it only works when both key and value objects implement equals() properly.

Map.equals()的工作方式是使用Object.equals()方法比较键和值这意味着只有当键和值对象都正确实现equals()时它才能工作。

For example, Map.equals() doesn’t work when the value type is array, as an array’s equals() method compares identity and not the contents of the array:

例如,当值类型为数组时,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. Using the Java Stream API

3.使用JavaStream API

We can also implement our own method to compare HashMaps using the Java 8 Stream API:

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

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

For simplicity, we implemented the areEqual() method that we can now use to compare HashMap<String, String> objects:

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

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

But we can also customize our own method areEqualWithArrayValue() to handle array values by using Arrays.equals() to compare two arrays:

但是我们也可以定制自己的方法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())));
}

Unlike Map.equals(), our own method will successfully compare HashMaps with array values:

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

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

4. Comparing HashMap Keys and Values

4.比较HashMap键和值

Next, let’s see how to compare two HashMap keys and their corresponding values.

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

4.1. Comparing HashMap Keys

4.1.比较HashMap

First, we can check if two HashMaps have same keys by just comparing their KeySet():

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

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

4.2. Comparing HashMap Values

4.2.比较HashMap

Next, we’ll see how to compare HashMap values one by one.

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

We’ll implement a simple method to check which keys have the same value in both HashMaps using Stream API:

我们将使用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()))));
}

We can now use areEqualKeyValues() to compare two different HashMaps to see in detail which keys have the same value and which have different values:

我们现在可以使用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. Map Difference Using Guava

5.使用Guava的地图差异

Finally, we’ll see how to get a detailed difference between two HashMaps using Guava Maps.difference().

最后,我们将看到如何使用Guava Maps.difference()获得两个HashMaps之间的详细差异。

This method returns a MapDifference object that has a number of useful methods to analyze the difference between the Maps. Let’s have a look at some of these.

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

5.1. MapDifference.entriesDiffering()

5.1.MapDifference. entriesDiffering()

First, we’ll obtain common keys that have different values in each HashMap using MapDifference.entriesDiffering():

首先,我们将使用MapDifference. entriesDiffering()获得在每个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());
}

The entriesDiffering() method returns a new Map that contains the set of common keys and ValueDifference objects as the set of values.

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

Each ValueDifference object has a leftValue() and rightValue() methods that return the values in the two Maps respectively.

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

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

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

Then, we can get entries that exist in only one HashMap using MapDifference.entriesOnlyOnRight() and 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()

5.3.MapDifference. entriesInCommon()

Next, we’ll get common entries using 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. Customizing Maps.difference() Behavior

5.4.定制Maps.difference()行为

Since Maps.difference() use equals() and hashCode() by default to compare entries, it won’t work for Objects that don’t implement them properly:

由于Maps.difference()默认使用equals()hashCode()来比较条目,它将不会对没有正确实现它们的Objects工作。

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

But, we can customize the method used in comparison using Equivalence.

但是,我们可以使用等价物定制比较中使用的方法。

For example, we’ll define Equivalence for type String[] to compare String[] values in our HashMaps as we like:

例如,我们将为String[]类型定义Equivalence,以便在我们的HashMaps中随意比较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. Conclusion

6.结论

In this article, we discussed different ways to compare HashMaps in Java. We learned multiple ways to check if two HashMaps are equal and how to get the detailed difference as well.

在这篇文章中,我们讨论了在Java中比较HashMaps的不同方法。我们学习了多种方法来检查两个HashMaps是否相等以及如何获得详细的差异。

The full source code is available over on GitHub.

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