When you compare two Java maps that are supposed to be equal, i.e. contain the same name/value pairs, you might want to give some details about potential mismatches, for example in your log output.
The Guava library from Google provides a convenient tool for that, namely the class com.google.common.collect.MapDifference.
In the sample code below I have implemented a simple utitily method that compares two maps and logs detailed error messages if they are not equal.
This code is also the first item in my guava-based github project.
Maven dependencies
Put this into pom.xml :
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
You will also need an slf4j implementation, like logback. For testing, we can use this :
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
And for the JUnit tests further below you will need this :
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
Java class
Save this as src/main/java/net/doepner/util/MapDiffUtil.java :
package net.doepner.util;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import static com.google.common.collect.MapDifference.ValueDifference;
/**
* Map comparison with detailed log messages
*/
public class MapDiffUtil {
private static final Logger log =
LoggerFactory.getLogger(MapDiffUtil.class);
public static <K, V> boolean validateEqual(
Map<K, V> map1, Map<K, V> map2,
String map1Name, String map2Name) {
final MapDifference<K, V> diff = Maps.difference(map1, map2);
if (diff.areEqual()) {
log.info("Maps '{}' and '{}' contain exactly the same "
+ "name/value pairs", map1Name, map2Name);
return true;
} else {
logKeys(diff.entriesOnlyOnLeft(), map1Name, map2Name);
logKeys(diff.entriesOnlyOnRight(), map2Name, map1Name);
logEntries(diff.entriesDiffering(), map1Name, map2Name);
return false;
}
}
private static <K, V> void logKeys(
Map<K, V> mapSubset, String n1, String n2) {
if (not(mapSubset.isEmpty())) {
log.error("Keys found in {} but not in {}: {}",
n1, n2, mapSubset.keySet());
}
}
private static <K, V> void logEntries(
Map<K, ValueDifference<V>> differing,
String n1, String n2) {
if (not(differing.isEmpty())) {
log.error("Differing values found {key={}-value,{}-value}: {}",
n1, n2, differing);
}
}
private static boolean not(boolean b) {
return !b;
}
}
Unit tests
Save this as src/main/java/net/doepner/util/MapDiffUtilTest.java :
package net.doepner.util;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests MapDiffUtil
*/
public class MapDiffUtilTest {
private final Logger log = LoggerFactory.getLogger(getClass());
@Rule
public TestName testName = new TestName();
@Before
public void logTestName() {
log.info("Executing {}", testName.getMethodName());
}
@Test
public void testEqual() {
final Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("A", 1);
map1.put("B", 2);
final Map<String, Integer> map2 = new HashMap<String, Integer>();
map2.put("B", 2);
map2.put("A", 1);
assertTrue("Maps should be equal", MapDiffUtil.validateEqual(
map1, map2, "map1", "map2"));
}
@Test
public void testSubset() {
final Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("A", 1);
final Map<String, Integer> map2 = new HashMap<String, Integer>();
map2.put("B", 2);
map2.put("A", 1);
assertFalse("Maps should be unequal", MapDiffUtil.validateEqual(
map1, map2, "map1", "map2"));
}
@Test
public void testSeparate() {
final Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("A", 1);
final Map<String, Integer> map2 = new HashMap<String, Integer>();
map2.put("B", 2);
assertFalse("Maps should be unequal", MapDiffUtil.validateEqual(
map1, map2, "map1", "map2"));
}
@Test
public void testMismatches() {
final Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("A", 1);
map1.put("B", 2);
final Map<String, Integer> map2 = new HashMap<String, Integer>();
map2.put("B", 20);
map2.put("C", 3);
assertFalse("Maps should be unequal", MapDiffUtil.validateEqual(
map1, map2, "map1", "map2"));
}
}