-
Bug
-
Resolution: Unresolved
-
P4
-
8
-
None
AbstractMap.equals() is implemented incorrectly, which can lead to incorrect results if called on a Map with a different membership contract. Since HashMap, TreeMap, and the unmodifiable maps from Map.of et. al. inherit AbstractMap.equals, they all have this problem too.
Example:
var umap = Map.of("a", 0, "A", 0);
var hash = new HashMap<>(umap)
var tree = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER)
tree.put("a", 0);
tree.put("x", 0);
We now have:
umap ==> {a=0, A=0}
hash ==> {a=0, A=0}
tree ==> {a=0, x=0}
However:
hash.equals(tree)
==> true *** !!! ***
tree.equals(hash)
==> false *** !!! ***
hash.entrySet().equals(tree.entrySet())
==> false
tree.entrySet().equals(hash.entrySet())
==> true
Also:
umap.equals(tree)
==> true *** !!! ***
tree.equals(umap)
==> false *** !!! ***
umap.entrySet().equals(tree.entrySet())
==> false
tree.entrySet().equals(umap.entrySet())
==> true
Results above marked with *** !!! *** are incorrect. The 'true' results not so marked are surprising but correct.
(ConcurrentHashMap does not have this problem, because it uses a different algorithm for equals(). This is possibly in order to accommodate the case where there are more than Integer.MAX_VALUE mappings. At such sizes, the size() method is clamped at MAX_VALUE and is thus unreliable.)
Example:
var umap = Map.of("a", 0, "A", 0);
var hash = new HashMap<>(umap)
var tree = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER)
tree.put("a", 0);
tree.put("x", 0);
We now have:
umap ==> {a=0, A=0}
hash ==> {a=0, A=0}
tree ==> {a=0, x=0}
However:
hash.equals(tree)
==> true *** !!! ***
tree.equals(hash)
==> false *** !!! ***
hash.entrySet().equals(tree.entrySet())
==> false
tree.entrySet().equals(hash.entrySet())
==> true
Also:
umap.equals(tree)
==> true *** !!! ***
tree.equals(umap)
==> false *** !!! ***
umap.entrySet().equals(tree.entrySet())
==> false
tree.entrySet().equals(umap.entrySet())
==> true
Results above marked with *** !!! *** are incorrect. The 'true' results not so marked are surprising but correct.
(ConcurrentHashMap does not have this problem, because it uses a different algorithm for equals(). This is possibly in order to accommodate the case where there are more than Integer.MAX_VALUE mappings. At such sizes, the size() method is clamped at MAX_VALUE and is thus unreliable.)
- relates to
-
JDK-6394757 (coll) AbstractSet.removeAll semantics are surprisingly dependent on relative sizes
-
- Open
-