8178355: IdentityHashMap uses identity-based comparison for values everywhere except remove(K,V) and replace(K,V,V)
Reviewed-by: smarks
This commit is contained in:
parent
25dfcbdea5
commit
53905e600a
@ -49,6 +49,10 @@ import jdk.internal.access.SharedSecrets;
|
||||
* designed for use only in the rare cases wherein reference-equality
|
||||
* semantics are required.</b>
|
||||
*
|
||||
* <p>The view collections of this map also have reference-equality semantics
|
||||
* for their elements. See the {@link keySet() keySet}, {@link values() values},
|
||||
* and {@link entrySet() entrySet} methods for further information.
|
||||
*
|
||||
* <p>A typical use of this class is <i>topology-preserving object graph
|
||||
* transformations</i>, such as serialization or deep-copying. To perform such
|
||||
* a transformation, a program must maintain a "node table" that keeps track
|
||||
@ -346,7 +350,8 @@ public class IdentityHashMap<K,V>
|
||||
|
||||
/**
|
||||
* Tests whether the specified object reference is a key in this identity
|
||||
* hash map.
|
||||
* hash map. Returns {@code true} if and only if this map contains a mapping
|
||||
* with key {@code k} such that {@code (key == k)}.
|
||||
*
|
||||
* @param key possible key
|
||||
* @return {@code true} if the specified object reference is a key
|
||||
@ -370,7 +375,8 @@ public class IdentityHashMap<K,V>
|
||||
|
||||
/**
|
||||
* Tests whether the specified object reference is a value in this identity
|
||||
* hash map.
|
||||
* hash map. Returns {@code true} if and only if this map contains a mapping
|
||||
* with value {@code v} such that {@code (value == v)}.
|
||||
*
|
||||
* @param value value whose presence in this map is to be tested
|
||||
* @return {@code true} if this map maps one or more keys to the
|
||||
@ -411,8 +417,9 @@ public class IdentityHashMap<K,V>
|
||||
|
||||
/**
|
||||
* Associates the specified value with the specified key in this identity
|
||||
* hash map. If the map previously contained a mapping for the key, the
|
||||
* old value is replaced.
|
||||
* hash map. If this map already {@link containsKey(Object) contains}
|
||||
* a mapping for the key, the old value is replaced, otherwise, a new mapping
|
||||
* is inserted into this map.
|
||||
*
|
||||
* @param key the key with which the specified value is to be associated
|
||||
* @param value the value to be associated with the specified key
|
||||
@ -497,8 +504,10 @@ public class IdentityHashMap<K,V>
|
||||
|
||||
/**
|
||||
* Copies all of the mappings from the specified map to this map.
|
||||
* These mappings will replace any mappings that this map had for
|
||||
* any of the keys currently in the specified map.
|
||||
* For each mapping in the specified map, if this map already
|
||||
* {@link containsKey(Object) contains} a mapping for the key,
|
||||
* its value is replaced with the value from the specified map;
|
||||
* otherwise, a new mapping is inserted into this map.
|
||||
*
|
||||
* @param m mappings to be stored in this map
|
||||
* @throws NullPointerException if the specified map is null
|
||||
@ -516,6 +525,8 @@ public class IdentityHashMap<K,V>
|
||||
|
||||
/**
|
||||
* Removes the mapping for this key from this map if present.
|
||||
* The mapping is removed if and only if the mapping has a key
|
||||
* {@code k} such that (key == k).
|
||||
*
|
||||
* @param key key whose mapping is to be removed from the map
|
||||
* @return the previous value associated with {@code key}, or
|
||||
@ -632,7 +643,9 @@ public class IdentityHashMap<K,V>
|
||||
* {@code true} if the given object is also a map and the two maps
|
||||
* represent identical object-reference mappings. More formally, this
|
||||
* map is equal to another map {@code m} if and only if
|
||||
* {@code this.entrySet().equals(m.entrySet())}.
|
||||
* {@code this.entrySet().equals(m.entrySet())}. See the
|
||||
* {@link entrySet() entrySet} method for the specification of equality
|
||||
* of this map's entries.
|
||||
*
|
||||
* <p><b>Owing to the reference-equality-based semantics of this map it is
|
||||
* possible that the symmetry and transitivity requirements of the
|
||||
@ -667,8 +680,11 @@ public class IdentityHashMap<K,V>
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this map. The hash code of a map is
|
||||
* defined to be the sum of the hash codes of each entry in the map's
|
||||
* {@code entrySet()} view. This ensures that {@code m1.equals(m2)}
|
||||
* defined to be the sum of the hash codes of each entry of this map.
|
||||
* See the {@link entrySet() entrySet} method for a specification of the
|
||||
* hash code of this map's entries.
|
||||
*
|
||||
* <p>This specification ensures that {@code m1.equals(m2)}
|
||||
* implies that {@code m1.hashCode()==m2.hashCode()} for any two
|
||||
* {@code IdentityHashMap} instances {@code m1} and {@code m2}, as
|
||||
* required by the general contract of {@link Object#hashCode}.
|
||||
@ -1162,7 +1178,9 @@ public class IdentityHashMap<K,V>
|
||||
* e.getValue()==o.getValue()}. To accommodate these equals
|
||||
* semantics, the {@code hashCode} method returns
|
||||
* {@code System.identityHashCode(e.getKey()) ^
|
||||
* System.identityHashCode(e.getValue())}.
|
||||
* System.identityHashCode(e.getValue())}. (While the keys and values
|
||||
* are compared using reference equality, the {@code Map.Entry}
|
||||
* objects themselves are not.)
|
||||
*
|
||||
* <p><b>Owing to the reference-equality-based semantics of the
|
||||
* {@code Map.Entry} instances in the set returned by this method,
|
||||
@ -1382,6 +1400,50 @@ public class IdentityHashMap<K,V>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>More formally, if this map contains a mapping from a key
|
||||
* {@code k} to a value {@code v} such that {@code (key == k)}
|
||||
* and {@code (value == v)}, then this method removes the mapping
|
||||
* for this key and returns {@code true}; otherwise it returns
|
||||
* {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
return removeMapping(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>More formally, if this map contains a mapping from a key
|
||||
* {@code k} to a value {@code v} such that {@code (key == k)}
|
||||
* and {@code (oldValue == v)}, then this method associates
|
||||
* {@code k} with {@code newValue} and returns {@code true};
|
||||
* otherwise it returns {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean replace(K key, V oldValue, V newValue) {
|
||||
Object k = maskNull(key);
|
||||
Object[] tab = table;
|
||||
int len = tab.length;
|
||||
int i = hash(k, len);
|
||||
|
||||
while (true) {
|
||||
Object item = tab[i];
|
||||
if (item == k) {
|
||||
if (tab[i + 1] != oldValue)
|
||||
return false;
|
||||
tab[i + 1] = newValue;
|
||||
return true;
|
||||
}
|
||||
if (item == null)
|
||||
return false;
|
||||
i = nextKeyIndex(i, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar form as array-based Spliterators, but skips blank elements,
|
||||
* and guestimates size as decreasing by half per split.
|
||||
|
@ -38,7 +38,7 @@ import static org.testng.Assert.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8285295
|
||||
* @bug 8285295 8178355
|
||||
* @summary Basic tests for IdentityHashMap
|
||||
* @run testng Basic
|
||||
*/
|
||||
@ -49,8 +49,6 @@ import static org.testng.Assert.*;
|
||||
// the identities of Map.Entry instances obtained from the entrySet; however, the keys and
|
||||
// values they contain are guaranteed to have the right identity.
|
||||
|
||||
// TODO remove(k, v)
|
||||
// TODO replace(k, v1, v2)
|
||||
// TODO add tests using null keys and values
|
||||
// TODO deeper testing of view collections including iterators, equals, contains, etc.
|
||||
// TODO Map.Entry::setValue
|
||||
@ -484,6 +482,97 @@ public class Basic {
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// remove(Object, Object) absent key, absent value
|
||||
@Test
|
||||
public void testRemoveAA() {
|
||||
Box k1c = new Box(k1a);
|
||||
Box v1c = new Box(v1a);
|
||||
assertFalse(map.remove(k1c, v1c));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1a, v1a),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// remove(Object, Object) absent key, present value
|
||||
@Test
|
||||
public void testRemoveAV() {
|
||||
Box k1c = new Box(k1a);
|
||||
assertFalse(map.remove(k1c, v1a));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1a, v1a),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// remove(Object, Object) present key, absent value
|
||||
@Test
|
||||
public void testRemoveKA() {
|
||||
Box v1c = new Box(v1a);
|
||||
assertFalse(map.remove(k1a, v1c));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1a, v1a),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// remove(Object, Object) present key, present value
|
||||
@Test
|
||||
public void testRemoveKV() {
|
||||
assertTrue(map.remove(k1a, v1a));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// replace(K, V, V) absent key, absent oldValue
|
||||
@Test
|
||||
public void testReplaceAA() {
|
||||
Box k1c = new Box(k1a);
|
||||
Box v1c = new Box(v1a);
|
||||
Box newVal = new Box(v2);
|
||||
assertFalse(map.replace(k1c, v1c, newVal));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1a, v1a),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// replace(K, V, V) absent key, present oldValue
|
||||
@Test
|
||||
public void testReplaceAV() {
|
||||
Box k1c = new Box(k1a);
|
||||
Box newVal = new Box(v2);
|
||||
assertFalse(map.replace(k1c, v1a, newVal));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1a, v1a),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// replace(K, V, V) present key, absent oldValue
|
||||
@Test
|
||||
public void testReplaceKA() {
|
||||
Box v1c = new Box(v1a);
|
||||
Box newVal = new Box(v2);
|
||||
assertFalse(map.replace(k1a, v1c, newVal));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1a, v1a),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// replace(K, V, V) present key, present oldValue
|
||||
@Test
|
||||
public void testReplaceKV() {
|
||||
Box newVal = new Box(v2);
|
||||
assertTrue(map.replace(k1a, v1a, newVal));
|
||||
checkEntries(map.entrySet(),
|
||||
entry(k1a, newVal),
|
||||
entry(k1b, v1b),
|
||||
entry(k2, v2));
|
||||
}
|
||||
|
||||
// AN: key absent, remappingFunction returns null
|
||||
@Test
|
||||
public void testComputeAN() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user