-
Bug
-
Resolution: Not an Issue
-
P4
-
8, 11, 12, 13
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
Introduced by the fix forJDK-4485486 / JDK-6197726, though probably did not work properly before either because these bugs blocked it.
The toArray() / toArray(T[]) methods of the Set returned by IdentityHashMap.entrySet() break the contract of the documentation of this method:
"containing all of the elements in this set"
with emphasis on "elements in this set"
toArray() actually returns different Entry objects, which are not even a valid substitute for the proper Entry objects because:
- They have no relation to the map anymore, calling setValue() has no effect
- They compare the stored key and value using `equals` instead of comparing references
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
import java.util.IdentityHashMap;
import java.util.Map.Entry;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
IdentityHashMap<String, String> identityHashMap = new IdentityHashMap<>();
final String key = "key";
final String oldValue = "oldValue";
final String newValue = "newValue";
identityHashMap.put(key, oldValue);
Set<Entry<String, String>> entrySet = identityHashMap.entrySet();
@SuppressWarnings("unchecked")
Entry<String, String> arrayEntry = (Entry<String, String>) entrySet.toArray()[0];
// Expecting that this changes value in map
arrayEntry.setValue(newValue);
// -> !! Did not change value
System.out.println("Expecting true: " + (identityHashMap.get(key) == newValue));
Entry<String, String> iteratorEntry = entrySet.iterator().next();
// Expecting that this changes value in map
iteratorEntry.setValue(newValue);
// -> Changed value
System.out.println("Expecting true: " + (identityHashMap.get(key) == newValue));
/*
* Set.toArray() contract is violated because here it does not return
* "all of the elements in this set", it actually returns different elements
*/
}
}
FREQUENCY : always
Introduced by the fix for
The toArray() / toArray(T[]) methods of the Set returned by IdentityHashMap.entrySet() break the contract of the documentation of this method:
"containing all of the elements in this set"
with emphasis on "elements in this set"
toArray() actually returns different Entry objects, which are not even a valid substitute for the proper Entry objects because:
- They have no relation to the map anymore, calling setValue() has no effect
- They compare the stored key and value using `equals` instead of comparing references
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
import java.util.IdentityHashMap;
import java.util.Map.Entry;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
IdentityHashMap<String, String> identityHashMap = new IdentityHashMap<>();
final String key = "key";
final String oldValue = "oldValue";
final String newValue = "newValue";
identityHashMap.put(key, oldValue);
Set<Entry<String, String>> entrySet = identityHashMap.entrySet();
@SuppressWarnings("unchecked")
Entry<String, String> arrayEntry = (Entry<String, String>) entrySet.toArray()[0];
// Expecting that this changes value in map
arrayEntry.setValue(newValue);
// -> !! Did not change value
System.out.println("Expecting true: " + (identityHashMap.get(key) == newValue));
Entry<String, String> iteratorEntry = entrySet.iterator().next();
// Expecting that this changes value in map
iteratorEntry.setValue(newValue);
// -> Changed value
System.out.println("Expecting true: " + (identityHashMap.get(key) == newValue));
/*
* Set.toArray() contract is violated because here it does not return
* "all of the elements in this set", it actually returns different elements
*/
}
}
FREQUENCY : always
- relates to
-
JDK-8178355 IdentityHashMap uses identity-based comparison for values everywhere except remove(K,V) and replace(K,V,V)
- Closed
-
JDK-8038146 Clarify Map.Entry's connection to the underlying map
- Resolved