The shortest way to compare two maps in Java is map1.equals(map2), which returns true when both maps contain the same key-value pairs, in any order and for any Map implementation. To compare only the keys, we compare keySet() views, and to find what is different, we use set operations on the keys or Guava’s Maps.difference().
We compare maps in tests that check a method’s result, in tools that show which configuration values changed between two releases, and in sync jobs that must find added, removed and changed records.
The following example compares two versions of an app configuration in the common ways, with the result of each line as a comment.
Map<String, String> v1 = Map.of("timeout", "30", "retries", "3", "theme", "light");
Map<String, String> v2 = Map.of("timeout", "60", "retries", "3", "cache", "on");
boolean sameEntries = v1.equals(v2); // false
boolean sameKeys = v1.keySet().equals(v2.keySet()); // false
boolean equalCopy = v1.equals(new TreeMap<>(v1)); // true (type and order do not matter)
Set<String> added = new TreeSet<>(v2.keySet());
added.removeAll(v1.keySet());
Set<String> addedKeys = added; // [cache]
MapDifference<String, String> diff = Maps.difference(v1, v2);
Map<String, MapDifference.ValueDifference<String>> changed = diff.entriesDiffering(); // {timeout=(30, 60)}
Notice that equals() only answers yes or no, while the key sets and MapDifference tell us what differs. We start with equals() and its traps, compare keys and values separately, build a full difference report in plain Java and with Guava, and finish with order-sensitive comparisons.
1. Compare Maps for Same Keys and Values
Two maps are equal when they hold the same set of key-value pairs. In detail, Map.equals() checks that both maps have the same size and that every entry of one map is also in the other, with values compared by equals().

1.1. Using Map.equals()
By default, equals() compares the key-value pairs and ignores the order of entries and the map class. A HashMap and a TreeMap with the same entries are equal, and so are two LinkedHashMap objects with the same entries in a different insertion order.
Map<String, Integer> limits = Map.of("free", 10, "pro", 100);
Map<String, Integer> sameLimits = new TreeMap<>(Map.of("pro", 100, "free", 10));
Map<String, Integer> otherLimits = Map.of("free", 10, "pro", 50);
boolean equal = limits.equals(sameLimits); // true
boolean different = limits.equals(otherLimits); // false
Because equals() calls equals() on every key and value, the result is only as good as those methods. Value classes need a correct equals(), which records provide for free, while a plain class without an equals() override compares object identity.
1.2. Comparing Maps With Array Type Values
Arrays do not override equals(), so two arrays with the same content are not equal, and two maps with array values compare as different. Arrays.equals() compares the content instead, and a small helper method applies it to every entry.
Map<String, String[]> hostsA = Map.of("eu", new String[] {"fra", "ams"});
Map<String, String[]> hostsB = Map.of("eu", new String[] {"fra", "ams"});
boolean byEquals = hostsA.equals(hostsB); // false (array identity)
static <K> boolean equalArrayValues(Map<K, String[]> first, Map<K, String[]> second) {
if (first.size() != second.size()) {
return false;
}
return first.entrySet().stream().allMatch(e -> second.containsKey(e.getKey()) && Arrays.equals(e.getValue(), second.get(e.getKey())));
}
Map<String, String[]> hostsA = Map.of("eu", new String[] {"fra", "ams"});
Map<String, String[]> hostsB = Map.of("eu", new String[] {"fra", "ams"});
boolean byContent = equalArrayValues(hostsA, hostsB); // true
The size check and containsKey() together make sure that both maps have the same keys. A cleaner fix is to store a List<String> instead of a String[], because lists compare by content and equals() works again.
2. Comparing Map Keys
Sometimes only the keys matter, for example when a test checks that a translation file defines every message key of the default language. The keySet() method returns a Set, and set operations answer every question about keys.
2.1. Both Maps Have Same Keys
Two key sets are equal when they have the same size and every element of one set is in the other. Set.equals() ignores the order and the values, so maps with the same keys and different values pass this check.
Map<String, String> v1 = Map.of("timeout", "30", "retries", "3");
Map<String, String> v2 = Map.of("timeout", "60", "retries", "3");
Map<String, String> v3 = Map.of("timeout", "30", "retries", "3", "cache", "on");
boolean sameKeys = v1.keySet().equals(v2.keySet()); // true
boolean moreKeys = v1.keySet().equals(v3.keySet()); // false
boolean covers = v3.keySet().containsAll(v1.keySet()); // true
2.2. Difference in Map Keys
To find the keys that only one map has, we copy the key set of that map and remove the keys of the other one. The copy matters, because removeAll() on the keySet() view itself would remove the entries from the map. Two copies give both directions, the added keys and the removed keys.
Map<String, String> v1 = Map.of("timeout", "30", "retries", "3", "theme", "light");
Map<String, String> v2 = Map.of("timeout", "60", "retries", "3", "cache", "on");
Set<String> onlyInV1 = new TreeSet<>(v1.keySet());
onlyInV1.removeAll(v2.keySet());
Set<String> removedKeys = onlyInV1; // [theme]
Set<String> onlyInV2 = new TreeSet<>(v2.keySet());
onlyInV2.removeAll(v1.keySet());
Set<String> addedKeys = onlyInV2; // [cache]
Set<String> common = new TreeSet<>(v1.keySet());
common.retainAll(v2.keySet());
Set<String> commonKeys = common; // [retries, timeout]
3. Comparing Map Values
Comparing only the values ignores which key each value belongs to. It is rare, but it shows up when two maps should hold the same items under different IDs. A map allows duplicate values, so we first decide whether {a=1, b=1} and {a=1, b=2} should count as having the same values.
3.1. Duplicate Values Are Allowed
When duplicates count, we compare how often each value appears. A frequency map from groupingBy() with counting() does that, and it does not depend on the iteration order of the maps. Copying values() into two lists and comparing the lists is not reliable, because the lists follow the iteration order of each map, and a HashMap has no defined order.
Map<String, Integer> stockA = Map.of("x1", 5, "x2", 5, "x3", 8);
Map<String, Integer> stockB = Map.of("y1", 8, "y2", 5, "y3", 5);
Map<String, Integer> stockC = Map.of("z1", 5, "z2", 8, "z3", 8);
Map<Integer, Long> countsA = stockA.values().stream().collect(Collectors.groupingBy(v -> v, Collectors.counting()));
boolean sameAsB = countsA.equals(stockB.values().stream().collect(Collectors.groupingBy(v -> v, Collectors.counting()))); // true
boolean sameAsC = countsA.equals(stockC.values().stream().collect(Collectors.groupingBy(v -> v, Collectors.counting()))); // false
3.2. Duplicate Values Are NOT Allowed
When only the distinct values matter, we copy the values into a HashSet, which drops the duplicates, and compare the sets. With that rule, stockA and stockC from the previous example are equal, because both contain only the values 5 and 8.
Map<String, Integer> stockA = Map.of("x1", 5, "x2", 5, "x3", 8);
Map<String, Integer> stockC = Map.of("z1", 5, "z2", 8, "z3", 8);
boolean sameDistinct = new HashSet<>(stockA.values()).equals(new HashSet<>(stockC.values())); // true
4. Finding the Difference Between Two Maps in Plain Java
A deployment tool shows a review page before each release, listing every configuration key that was added, removed or changed since the last release. The added and removed keys come from the key set operations in section 2.2, and the changed keys are the common keys whose values differ.
record Change(String before, String after) {}
Map<String, String> v1 = Map.of("timeout", "30", "retries", "3", "theme", "light");
Map<String, String> v2 = Map.of("timeout", "60", "retries", "3", "cache", "on");
Map<String, Change> changes = v1.keySet().stream().filter(v2::containsKey).filter(k -> !Objects.equals(v1.get(k), v2.get(k))).collect(Collectors.toMap(k -> k, k -> new Change(v1.get(k), v2.get(k)), (a, b) -> a, TreeMap::new)); // {timeout=Change[before=30, after=60]}
The call Objects.equals() treats two null values as equal and does not throw when one value is null, which matters for a HashMap that allows null values. The TreeMap supplier sorts the report by key, so it reads the same on every run.
5. Map Difference With Guava
Guava’s Maps.difference() computes the whole comparison in one call and returns a MapDifference object. It sorts every key into one of four groups, the same groups as in the diagram in section 1. We add the current Guava version to the build.
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.7.2-jre</version>
</dependency>
Each group has its own method, and areEqual() returns true only when the last three groups are empty.
Map<String, String> v1 = new TreeMap<>(Map.of("timeout", "30", "retries", "3", "theme", "light"));
Map<String, String> v2 = new TreeMap<>(Map.of("timeout", "60", "retries", "3", "cache", "on"));
MapDifference<String, String> diff = Maps.difference(v1, v2);
boolean equal = diff.areEqual(); // false
Map<String, String> inCommon = diff.entriesInCommon(); // {retries=3}
Map<String, String> onlyLeft = diff.entriesOnlyOnLeft(); // {theme=light}
Map<String, String> onlyRight = diff.entriesOnlyOnRight(); // {cache=on}
Map<String, MapDifference.ValueDifference<String>> differing = diff.entriesDiffering(); // {timeout=(30, 60)}
String oldTimeout = differing.get("timeout").leftValue(); // "30"
Guava is worth the dependency when the project already uses it or needs the full report often. For a single check in a test, the plain Java code in section 4 does the same without a library.
6. Comparing Maps Where the Order Matters
The equals() method never looks at the order, not even for a LinkedHashMap or a TreeMap. When the order is part of the expected result, for example the order of columns in a CSV export, we compare the entries as lists, because List.equals() checks the position of each element.
Map<String, Integer> columnsA = new LinkedHashMap<>();
columnsA.put("name", 20);
columnsA.put("email", 30);
Map<String, Integer> columnsB = new LinkedHashMap<>();
columnsB.put("email", 30);
columnsB.put("name", 20);
boolean unordered = columnsA.equals(columnsB); // true
boolean ordered = new ArrayList<>(columnsA.entrySet()).equals(new ArrayList<>(columnsB.entrySet())); // false
Since Java 21, a LinkedHashMap is also a SequencedMap, so the same check works for the reversed order with sequencedEntrySet().reversed().
7. Compare Two Maps in Java FAQs
The follow-up questions about comparing maps are mostly about order, map classes, BigDecimal values and tests.
7.1. Does HashMap.equals() Check the Order of Entries?
No. Map.equals() compares only the key-value pairs, so the order never matters, for any map class. To include the order, compare the entry sets as lists, as section 6 shows.
7.2. Is a HashMap Equal to a TreeMap With the Same Entries?
Yes. The equals() contract of Map does not depend on the implementation class, so any two maps with the same entries are equal, and their hashCode() values are equal too.
7.3. Why Are Two Maps With BigDecimal Values Not Equal?
Because BigDecimal.equals() also compares the scale, so 1.0 and 1.00 are different values. Maps of prices or amounts from different sources often fail equals() for that reason, and we compare the values with compareTo() instead.
Map<String, BigDecimal> priceA = Map.of("pen", new BigDecimal("1.0"));
Map<String, BigDecimal> priceB = Map.of("pen", new BigDecimal("1.00"));
boolean byEquals = priceA.equals(priceB); // false
boolean byValue = priceA.keySet().equals(priceB.keySet()) && priceA.entrySet().stream().allMatch(e -> e.getValue().compareTo(priceB.get(e.getKey())) == 0); // true
7.4. How Do I Compare Maps in JUnit Tests?
Use assertEquals(expected, actual), which calls equals() and prints both maps on failure. For a readable failure message on large maps, AssertJ’s assertThat(actual).containsExactlyInAnyOrderEntriesOf(expected) lists the missing and unexpected entries.
8. Conclusion
For most comparisons, map1.equals(map2) is the right call. It checks that both maps hold the same key-value pairs, ignores the order and the map class, and depends on correct equals() methods in the keys and values, which arrays and BigDecimal scales break.
To compare only keys, we compare the keySet() views, and to find the differences, we copy a key set and remove the other map’s keys. For a full report of added, removed and changed entries, a short stream or Guava’s Maps.difference() sorts every key into its group.
9. References
- Map.equals() Javadoc (Java 25)
- Set.equals() Javadoc (Java 25)
- Guava MapDifference Javadoc
- BigDecimal.equals() Javadoc (Java 25)
Happy Learning !!
In Section “3.1. Duplicate Values Are NOT Allowed”, the code is written for without removing the duplicate values but the title is opposite . On the other hand in section “3.2. Duplicate Values Are Allowed “, the code is written for removing the duplicate values but the title is opposite. Means , the titles should be interchange so they suits the code .Correct me please if I am understanding it wrong.
Thanks
You are right. The headings have been updated. Thanks for noticing and letting me know. Much appreciated.