JDK-6588783 : (coll) IdentityHashMap.removeAll
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 6,9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2007-08-02
  • Updated: 2022-04-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
None - is a plain java bug

A DESCRIPTION OF THE PROBLEM :
I think I found a Bug in the IdentityHashMap collection: The removeAll Method removes Object by equality not by there Sytem.identityHashCode. Here is the code from the class: (line 411)

public boolean removeAll(Collection<?> c) {
boolean modified = false;
for (Iterator i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {
i.remove();
modified = true;
}
}
return modified;
}

As you can see, the hashMap delegates to c.contains() which can be any collection. IMHO it must be:

@Override
public boolean removeAll(Collection<?> c) {
boolean modified = false;
for (Iterator i = c.iterator(); i.hasNext(); ) {
Object object = i.next();
if (PIdentityHashMap.this.containsKey(object)) {
PIdentityHashMap.this.remove(object);
modified = true;
}
}
return modified;
}

We iterate over the collection and not the entries of the map. Here is a little TestCase:

public void test_show_identityHashMap_Bug() {
List<WrongHashCode> removed = new ArrayList<WrongHashCode>();
removed.add(new WrongHashCode("1"));

IdentityHashMap<WrongHashCode, String> hashMap = new IdentityHashMap<WrongHashCode, String>();
hashMap.put(new WrongHashCode("2"), "");

assertFalse(hashMap.keySet().removeAll(removed));
assertEquals(1, hashMap.size());
}

The class WrongHashCode defines hashCode and equals in the following (wrong (-: ) way:

@Override
public int hashCode() {
return 0;
}

@Override
public boolean equals(Object object) {
return true;
}

I patched the class, so I can work on my software.


REPRODUCIBILITY :
This bug can be reproduced always.

Comments
The API documentation of Collection.removeAll states: * Removes all of this collection's elements that are also contained in the * specified collection (optional operation). This means that the behavior of the removeAll method in IdentityHashMap.KeySet is correct. For the desired effect, the user should pass an identity collection to the list, or alternatively call removeIf instead. In addition, the desired effect should be obtainable when the user passes this identity key set to the removeAll of another collection.
15-04-2022

I think the bug report is correct though I am concerned that there are likely to be compatibility concerns and, for retainAll(), performance concerns. Here's the implementations I came up with. /* * Uses the IdentityHashMap element equality semantics. */ public boolean removeAll(Collection<?> c) { int oldSize = size; c.forEach((each) -> { IdentityHashMap.this.remove(each); }); return size != oldSize; } /* * Uses the IdentityHashMap element equality semantics. */ public boolean retainAll(Collection<?> c) { if (!(c instanceof IdentityHashMap.KeySet)) { // we need a collection with the same element eqality semantics. Collector<Object, ?, Set<Object>> collector = Collectors.toCollection(()-> { return Collections.newSetFromMap( new IdentityHashMap<Object,Boolean>()); }); c = c.stream().filter(this::contains).collect(collector); } int oldSize = size; Iterator<K> i = iterator(); while(i.hasNext()) { if (!c.contains(i.next())) { i.remove(); } } return size != oldSize; }
06-05-2014

EVALUATION Hmmmm.... The authors of IdentityHashMap clearly considered the case where the argument collection does not use identity comparisons.
03-08-2007