diff --git a/src/java.base/share/classes/java/util/IdentityHashMap.java b/src/java.base/share/classes/java/util/IdentityHashMap.java index b82266df390..77f06fb9410 100644 --- a/src/java.base/share/classes/java/util/IdentityHashMap.java +++ b/src/java.base/share/classes/java/util/IdentityHashMap.java @@ -49,6 +49,10 @@ import jdk.internal.access.SharedSecrets; * designed for use only in the rare cases wherein reference-equality * semantics are required. * + *
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. + * *
A typical use of this class is topology-preserving object graph
* transformations, 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 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 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 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 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}
+ *
+ * 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.
diff --git a/test/jdk/java/util/IdentityHashMap/Basic.java b/test/jdk/java/util/IdentityHashMap/Basic.java
index 6af11d53b43..a002f537397 100644
--- a/test/jdk/java/util/IdentityHashMap/Basic.java
+++ b/test/jdk/java/util/IdentityHashMap/Basic.java
@@ -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() {