Comparando dois HashMaps em Java

Comparando dois HashMaps em Java

1. Visão geral

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

Discutiremos várias maneiras de verificar se doisHashMaps são semelhantes. Também usaremos Java 8 Stream API e Guava para obter as diferenças detalhadas entre diferentesHashMaps.

2. UsandoMap.equals()

Primeiro, usaremosMap.equals() para verificar se doisHashMaps têm as mesmas entradas:

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

Aqui, estamos criando três objetosHashMap e adicionando entradas. Então, estamos usandoMap.equals() para verificar se doisHashMaps têm as mesmas entradas.

The way that Map.equals() works is by comparing keys and values using the Object.equals()method. Isso significa que só funciona quando os objetos chave e valor implementamequals() corretamente.

Por exemplo,Map.equals() não funciona quando o tipo de valor é array, já que o métodoequals() de um array compara a identidade e não o conteúdo do array:

@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. Usando a API JavaStream

Também podemos implementar nosso próprio método para compararHashMaps usando a 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())));
}

Para simplificar, implementamos o métodoareEqual() que agora podemos usar para comparar objetosHashMap<String, String>:

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

Mas também podemos personalizar nosso próprio métodoareEqualWithArrayValue() para lidar com valores de array usandoArrays.equals() para comparar dois arrays:

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

Ao contrário deMap.equals(), nosso próprio método irá comparar com sucessoHashMaps com valores de matriz:

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

4. ComparandoHashMap chaves e valores

A seguir, vamos ver como comparar duas chavesHashMap e seus valores correspondentes.

4.1. Comparando chaves deHashMap

Primeiro, podemos verificar se doisHashMaps têm as mesmas chaves, apenas comparando seusKeySet():

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

4.2. Comparando valores deHashMap

A seguir, veremos como comparar os valores deHashMap um por um.

Implementaremos um método simples para verificar quais chaves têm o mesmo valor emHashMaps usando a APIStream:

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

Agora podemos usarareEqualKeyValues() para comparar doisHashMaps diferentes para ver em detalhes quais chaves têm o mesmo valor e quais têm valores diferentes:

@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. Diferença do mapa usando goiaba

Finalmente, veremoshow to get a detailed difference between two HashMaps using Guava Maps.difference().

Este método retorna um objetoMapDifference que tem uma série de métodos úteis para analisar a diferença entre osMaps.. Vamos dar uma olhada em alguns deles.

5.1. MapDifference.entriesDiffering()

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

O métodoentriesDiffering() retorna um novoMap que contém o conjunto de chaves comuns e objetosValueDifference como o conjunto de valores.

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

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

Então, podemos obter entradas que existem em apenas umHashMap usandoMapDifference.entriesOnlyOnRight()eMapDifference.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()

A seguir,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. Personalizando o comportamento deMaps.difference()

ComoMaps.difference() usaequals() ehashCode() por padrão para comparar entradas, não funcionará para objetos que não os implementam corretamente:

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

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

Por exemplo, definiremosEquivalence  para o tipoString[] para comparar os valores deString[] em nossoHashMaps como quisermos:

@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. Conclusão

Neste artigo, discutimos diferentes maneiras de compararHashMaps em Java. Aprendemos várias maneiras de verificar se doisHashMaps são iguais e como obter a diferença detalhada também.

O código-fonte completo está disponívelover on GitHub.