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.</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.
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() {