Comparing Two HashMaps in Java

Сравнение двух HashMaps в Java

1. обзор

В этом руководствеwe’re going to explore different ways to compare two HashMaps in Java.

Мы обсудим несколько способов проверить, похожи ли дваHashMaps. Мы также будем использовать Java 8 Stream API и Guava, чтобы получить подробные различия между разнымиHashMaps.

2. ИспользуяMap.equals()

Во-первых, мы воспользуемсяMap.equals(), чтобы проверить, имеют ли дваHashMaps одинаковые записи:

@Test
public void whenCompareTwoHashMapsUsingEquals_thenSuccess() {
    Map asiaCapital1 = new HashMap();
    asiaCapital1.put("Japan", "Tokyo");
    asiaCapital1.put("South Korea", "Seoul");

    Map asiaCapital2 = new HashMap();
    asiaCapital2.put("South Korea", "Seoul");
    asiaCapital2.put("Japan", "Tokyo");

    Map asiaCapital3 = new HashMap();
    asiaCapital3.put("Japan", "Tokyo");
    asiaCapital3.put("China", "Beijing");

    assertTrue(asiaCapital1.equals(asiaCapital2));
    assertFalse(asiaCapital1.equals(asiaCapital3));
}

Здесь мы создаем три объектаHashMap и добавляем записи. Затем мы используемMap.equals(), чтобы проверить, имеют ли дваHashMaps одинаковые записи.

The way that Map.equals() works is by comparing keys and values using the Object.equals()method. Это означает, что он работает только тогда, когда и ключевые, и значения объекты реализуютequals() правильно.

Например,Map.equals() не работает, если тип значения - массив, поскольку метод массиваequals() сравнивает идентичность, а не содержимое массива:

@Test
public void whenCompareTwoHashMapsWithArrayValuesUsingEquals_thenFail() {
    Map asiaCity1 = new HashMap();
    asiaCity1.put("Japan", new String[] { "Tokyo", "Osaka" });
    asiaCity1.put("South Korea", new String[] { "Seoul", "Busan" });

    Map asiaCity2 = new HashMap();
    asiaCity2.put("South Korea", new String[] { "Seoul", "Busan" });
    asiaCity2.put("Japan", new String[] { "Tokyo", "Osaka" });

    assertFalse(asiaCity1.equals(asiaCity2));
}

3. Использование API JavaStream

Мы также можем реализовать наш собственный метод для сравненияHashMaps, используя API Java 8Stream:

private boolean areEqual(Map first, Map 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 first, Map 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

Во-первых, мы можем проверить, имеют ли дваHashMapsодинаковые ключи, просто сравнив ихKeySet():

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

4.2. Сравнение значенийHashMap

Далее мы посмотрим, как сравнивать значенияHashMap одно за другим.

Мы реализуем простой метод, чтобы проверить, какие ключи имеют одинаковое значение в обоихHashMaps, используяStream API:

private Map areEqualKeyValues(Map first, Map 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 asiaCapital3 = new HashMap();
    asiaCapital3.put("Japan", "Tokyo");
    asiaCapital3.put("South Korea", "Seoul");
    asiaCapital3.put("China", "Beijing");

    Map asiaCapital4 = new HashMap();
    asiaCapital4.put("South Korea", "Seoul");
    asiaCapital4.put("Japan", "Osaka");
    asiaCapital4.put("China", "Beijing");

    Map 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. Разница в карте с использованием гуавы

Наконец, мы увидимhow to get a detailed difference between two HashMaps using Guava Maps.difference().

Этот метод возвращает объектMapDifference, в котором есть несколько полезных методов для анализа разницы междуMaps.. Давайте посмотрим на некоторые из них.

5.1. MapDifference.entriesDiffering()с

Сначала получимcommon keys that have different values in each HashMap using MapDifference.entriesDiffering():

@Test
public void givenDifferentMaps_whenGetDiffUsingGuava_thenSuccess() {
    Map asia1 = new HashMap();
    asia1.put("Japan", "Tokyo");
    asia1.put("South Korea", "Seoul");
    asia1.put("India", "New Delhi");

    Map asia2 = new HashMap();
    asia2.put("Japan", "Tokyo");
    asia2.put("China", "Beijing");
    asia2.put("India", "Delhi");

    MapDifference diff = Maps.difference(asia1, asia2);
    Map> 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 в качестве набора значений.

Each ValueDifference object has a leftValue() and rightValue() methods that return the values in the two Maps соответственно.

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

Затем мы можем получить записи, которые существуют только в одномHashMap, используяMapDifference.entriesOnlyOnRight() иMapDifference.entriesOnlyOnLeft():

@Test
public void givenDifferentMaps_whenGetEntriesOnOneSideUsingGuava_thenSuccess() {
    MapDifference diff = Maps.difference(asia1, asia2);
    Map entriesOnlyOnRight = diff.entriesOnlyOnRight();
    Map 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()с

Далееwe’ll get common entries using MapDifference.entriesInCommon():

@Test
public void givenDifferentMaps_whenGetCommonEntriesUsingGuava_thenSuccess() {
    MapDifference diff = Maps.difference(asia1, asia2);
    Map entriesInCommon = diff.entriesInCommon();

    assertEquals(1, entriesInCommon.size());
    assertThat(entriesInCommon, hasEntry("Japan", "Tokyo"));
}

5.4. Настройка поведенияMaps.difference()

ПосколькуMaps.difference() по умолчанию используетequals() иhashCode() для сравнения записей, это не будет работать для объектов, которые не реализуют их должным образом:

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

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

Например, мы определимEquivalence  для типаString[], чтобы сравнивать значенияString[] в нашемHashMaps, как нам нравится:

@Test
public void givenSimilarMapsWithArrayValue_whenCompareUsingGuavaEquivalence_thenSuccess() {
    Equivalence eq = new Equivalence() {
        @Override
        protected boolean doEquivalent(String[] a, String[] b) {
            return Arrays.equals(a, b);
        }

        @Override
        protected int doHash(String[] value) {
            return value.hashCode();
        }
    };

    MapDifference diff = Maps.difference(asiaCity1, asiaCity2, eq);
    assertTrue(diff.areEqual());

    diff = Maps.difference(asiaCity1, asiaCity3, eq);
    assertFalse(diff.areEqual());
}

6. Заключение

В этой статье мы обсудили различные способы сравненияHashMaps в Java. Мы узнали несколько способов проверить, равны ли дваHashMaps, а также как получить подробную разницу.

Доступен полный исходный кодover on GitHub.