As previously trailed [1], please find below a suggested change to
address the problem reported in bug 6312706 [2], together with testcases
to demonstrate the veracity of the change.

The change causes the entry set iterators for java.util.EnumMap and
java.util.IdentityHashMap to return distinct Map.Entry objects for each
call to next().

As detailed in the bug report, by doing this, the entry set, its
iterator, and the returned Entry objects behave in the manner in which
one would expect them to, without weird unexpected edge conditions.

To mitigate the overhead of producing new objects for each Entry
returned, I've also expanded the implementation of EnumMap's equals(),
hashCode() and writeObject(), such that they avoid using its entry set
to traverse through the entries, and added suitable tests to demonstrate
these expanded forms still behave properly.

I've also added a testcase to demonstrate that the problem being fixed
here does not occur in java.util.concurrent.ConcurrentHashMap .
(It was suggested in the bug report that the problem also occurred
there, so I thought it worthwhile to include the testcase to demonstrate
that it doesn't).

As always, and comments / advice / suggestions gratefully received,
Thanks,
Neil

[1] 
http://mail.openjdk.java.net/pipermail/core-libs-dev/2011-January/005763.html
[2] http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6312706

-- 
Unless stated above:
IBM email: neil_richards at uk.ibm.com
IBM United Kingdom Limited - Registered in England and Wales with number 741598.
Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU


# HG changeset patch
# User Neil Richards <neil.richa...@ngmr.net>, <neil_richa...@uk.ibm.com>
# Date 1298984147 0
# Branch j6312706
# Node ID 98616c2a19c4884bfadbc1dba032cb60fbcfe275
# Parent  554adcfb615e63e62af530b1c10fcf7813a75b26
6312706: Map entrySet iterators should return different entries on each call to 
next()
Summary: Return distinct entry objects from entrySet iterators of EnumMap, 
IdentityHashMap
Contributed-by: <neil.richa...@ngmr.net>

diff -r 554adcfb615e -r 98616c2a19c4 src/share/classes/java/util/EnumMap.java
--- a/src/share/classes/java/util/EnumMap.java  Wed Mar 16 15:01:07 2011 -0700
+++ b/src/share/classes/java/util/EnumMap.java  Tue Mar 01 12:55:47 2011 +0000
@@ -106,7 +106,11 @@
     /**
      * Distinguished non-null value for representing null values.
      */
-    private static final Object NULL = new Object();
+    private static final Object NULL = new Object() { 
+        public int hashCode() {
+            return 0;
+        }
+    };
 
     private Object maskNull(Object value) {
         return (value == null ? NULL : value);
@@ -464,6 +468,7 @@
         public Iterator<Map.Entry<K,V>> iterator() {
             return new EntryIterator();
         }
+
         public boolean contains(Object o) {
             if (!(o instanceof Map.Entry))
                 return false;
@@ -552,70 +557,82 @@
         }
     }
 
-    /**
-     * Since we don't use Entry objects, we use the Iterator itself as entry.
-     */
-    private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>>
-        implements Map.Entry<K,V>
-    {
+    private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> {
+        private Entry lastReturnedEntry = null;
+
         public Map.Entry<K,V> next() {
             if (!hasNext())
                 throw new NoSuchElementException();
-            lastReturnedIndex = index++;
-            return this;
+            lastReturnedEntry = new Entry(index++);
+            return lastReturnedEntry;
         }
 
-        public K getKey() {
-            checkLastReturnedIndexForEntryUse();
-            return keyUniverse[lastReturnedIndex];
+        public void remove() {
+            lastReturnedIndex = 
+                ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
+            super.remove();
+            lastReturnedEntry.index = lastReturnedIndex;
+            lastReturnedEntry = null;
         }
+    
+        private class Entry implements Map.Entry<K,V> {
+            private int index;
 
-        public V getValue() {
-            checkLastReturnedIndexForEntryUse();
-            return unmaskNull(vals[lastReturnedIndex]);
-        }
+            private Entry(int index) {
+                this.index = index;
+            }
 
-        public V setValue(V value) {
-            checkLastReturnedIndexForEntryUse();
-            V oldValue = unmaskNull(vals[lastReturnedIndex]);
-            vals[lastReturnedIndex] = maskNull(value);
-            return oldValue;
-        }
+            public K getKey() {
+                checkIndexForEntryUse();
+                return keyUniverse[index];
+            }
 
-        public boolean equals(Object o) {
-            if (lastReturnedIndex < 0)
-                return o == this;
+            public V getValue() {
+                checkIndexForEntryUse();
+                return unmaskNull(vals[index]);
+            }
 
-            if (!(o instanceof Map.Entry))
-                return false;
-            Map.Entry e = (Map.Entry)o;
-            V ourValue = unmaskNull(vals[lastReturnedIndex]);
-            Object hisValue = e.getValue();
-            return e.getKey() == keyUniverse[lastReturnedIndex] &&
-                (ourValue == hisValue ||
-                 (ourValue != null && ourValue.equals(hisValue)));
-        }
+            public V setValue(V value) {
+                checkIndexForEntryUse();
+                V oldValue = unmaskNull(vals[index]);
+                vals[index] = maskNull(value);
+                return oldValue;
+            }
 
-        public int hashCode() {
-            if (lastReturnedIndex < 0)
-                return super.hashCode();
+            public boolean equals(Object o) {
+                if (index < 0)
+                    return o == this;
 
-            Object value = vals[lastReturnedIndex];
-            return keyUniverse[lastReturnedIndex].hashCode()
-                ^ (value == NULL ? 0 : value.hashCode());
-        }
+                if (!(o instanceof Map.Entry))
+                    return false;
 
-        public String toString() {
-            if (lastReturnedIndex < 0)
-                return super.toString();
+                Map.Entry e = (Map.Entry)o;
+                V ourValue = unmaskNull(vals[index]);
+                Object hisValue = e.getValue();
+                return (e.getKey() == keyUniverse[index] &&
+                        (ourValue == hisValue ||
+                         (ourValue != null && ourValue.equals(hisValue))));
+            }
 
-            return keyUniverse[lastReturnedIndex] + "="
-                + unmaskNull(vals[lastReturnedIndex]);
-        }
+            public int hashCode() {
+                if (index < 0)
+                    return super.hashCode();
 
-        private void checkLastReturnedIndexForEntryUse() {
-            if (lastReturnedIndex < 0)
-                throw new IllegalStateException("Entry was removed");
+                return entryHashCode(index);
+            }
+
+            public String toString() {
+                if (index < 0)
+                    return super.toString();
+
+                return keyUniverse[index] + "="
+                    + unmaskNull(vals[index]);
+            }
+
+            private void checkIndexForEntryUse() {
+                if (index < 0)
+                    throw new IllegalStateException("Entry was removed");
+            }
         }
     }
 
@@ -631,10 +648,35 @@
      * @return <tt>true</tt> if the specified object is equal to this map
      */
     public boolean equals(Object o) {
-        if (!(o instanceof EnumMap))
-            return super.equals(o);
+        if (this == o) 
+            return true;
+        if (o instanceof EnumMap)
+            return equals((EnumMap)o);
+        if (!(o instanceof Map))
+            return false;
+        
+        Map<K,V> m = (Map<K,V>)o;
+        if (size != m.size())
+            return false;
 
-        EnumMap em = (EnumMap)o;
+        for (int i = 0; i < keyUniverse.length; i++) {
+            if (null != vals[i]) {
+                K key = keyUniverse[i];
+                V value = unmaskNull(vals[i]);
+                if (null == value) {
+                    if (!((null == m.get(key)) && m.containsKey(key)))
+                       return false;
+                } else {
+                   if (!value.equals(m.get(key)))
+                      return false;
+                }
+            } 
+        }
+
+        return true;
+    }
+
+    private boolean equals(EnumMap em) {
         if (em.keyType != keyType)
             return size == 0 && em.size == 0;
 
@@ -650,6 +692,26 @@
     }
 
     /**
+     * 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.
+     */
+    public int hashCode() {
+        int h = 0;
+
+        for (int i = 0; i < keyUniverse.length; i++) {
+            if (null != vals[i]) {
+                h += entryHashCode(i);
+            }
+        }
+
+        return h;
+    }
+
+    private int entryHashCode(int index) {
+        return (keyUniverse[index].hashCode() ^ vals[index].hashCode());
+    }
+
+    /**
      * Returns a shallow copy of this enum map.  (The values themselves
      * are not cloned.
      *
@@ -705,9 +767,13 @@
         s.writeInt(size);
 
         // Write out keys and values (alternating)
-        for (Map.Entry<K,V> e :  entrySet()) {
-            s.writeObject(e.getKey());
-            s.writeObject(e.getValue());
+        int entriesToBeWritten = size;
+        for (int i = 0; entriesToBeWritten > 0; i++) {
+            if (null != vals[i]) {
+                s.writeObject(keyUniverse[i]);
+                s.writeObject(unmaskNull(vals[i]));
+                entriesToBeWritten--;
+            }
         }
     }
 
diff -r 554adcfb615e -r 98616c2a19c4 
src/share/classes/java/util/IdentityHashMap.java
--- a/src/share/classes/java/util/IdentityHashMap.java  Wed Mar 16 15:01:07 
2011 -0700
+++ b/src/share/classes/java/util/IdentityHashMap.java  Tue Mar 01 12:55:47 
2011 +0000
@@ -829,71 +829,82 @@
         }
     }
 
-    /**
-     * Since we don't use Entry objects, we use the Iterator
-     * itself as an entry.
-     */
     private class EntryIterator
         extends IdentityHashMapIterator<Map.Entry<K,V>>
-        implements Map.Entry<K,V>
     {
+        private Entry lastReturnedEntry = null;
+
         public Map.Entry<K,V> next() {
-            nextIndex();
-            return this;
+            lastReturnedEntry = new Entry(nextIndex());
+            return lastReturnedEntry;
         }
 
-        public K getKey() {
-            // Provide a better exception than out of bounds index
-            if (lastReturnedIndex < 0)
-                throw new IllegalStateException("Entry was removed");
+        public void remove() {
+            lastReturnedIndex = 
+                ((0 == lastReturnedIndex) ? -1 : lastReturnedEntry.index);
+            super.remove();
+            lastReturnedEntry.index = lastReturnedIndex;
+            lastReturnedEntry = null;
+        }
+    
+        private class Entry implements Map.Entry<K,V> {
+            private int index;
 
-            return (K) unmaskNull(traversalTable[lastReturnedIndex]);
-        }
+            private Entry(int index) {
+                this.index = index;
+            }
 
-        public V getValue() {
-            // Provide a better exception than out of bounds index
-            if (lastReturnedIndex < 0)
-                throw new IllegalStateException("Entry was removed");
+            public K getKey() {
+                checkIndexForEntryUse();
+                return (K) unmaskNull(traversalTable[index]);
+            }
 
-            return (V) traversalTable[lastReturnedIndex+1];
-        }
+            public V getValue() {
+                checkIndexForEntryUse();
+                return (V) traversalTable[index+1];
+            }
 
-        public V setValue(V value) {
-            // It would be mean-spirited to proceed here if remove() called
-            if (lastReturnedIndex < 0)
-                throw new IllegalStateException("Entry was removed");
-            V oldValue = (V) traversalTable[lastReturnedIndex+1];
-            traversalTable[lastReturnedIndex+1] = value;
-            // if shadowing, force into main table
-            if (traversalTable != IdentityHashMap.this.table)
-                put((K) traversalTable[lastReturnedIndex], value);
-            return oldValue;
-        }
+            public V setValue(V value) {
+                checkIndexForEntryUse();
+                V oldValue = (V) traversalTable[index+1];
+                traversalTable[index+1] = value;
+                // if shadowing, force into main table
+                if (traversalTable != IdentityHashMap.this.table)
+                    put((K) traversalTable[index], value);
+                return oldValue;
+            }
 
-        public boolean equals(Object o) {
-            if (lastReturnedIndex < 0)
-                return super.equals(o);
+            public boolean equals(Object o) {
+                if (index < 0)
+                    return super.equals(o);
 
-            if (!(o instanceof Map.Entry))
-                return false;
-            Map.Entry e = (Map.Entry)o;
-            return e.getKey()   == getKey() &&
-                   e.getValue() == getValue();
-        }
+                if (!(o instanceof Map.Entry))
+                    return false;
+                Map.Entry e = (Map.Entry)o;
+                return (e.getKey() == unmaskNull(traversalTable[index]) &&
+                       e.getValue() == traversalTable[index+1]);
+            }
 
-        public int hashCode() {
-            if (lastReturnedIndex < 0)
-                return super.hashCode();
+            public int hashCode() {
+                if (lastReturnedIndex < 0)
+                    return super.hashCode();
 
-            return System.identityHashCode(getKey()) ^
-                   System.identityHashCode(getValue());
-        }
+                return 
(System.identityHashCode(unmaskNull(traversalTable[index])) ^
+                       System.identityHashCode(traversalTable[index+1]));
+            }
 
-        public String toString() {
-            if (lastReturnedIndex < 0)
-                return super.toString();
+            public String toString() {
+                if (index < 0)
+                    return super.toString();
 
-            return getKey() + "=" + getValue();
+                return (unmaskNull(traversalTable[index]) + "=" 
+                        + traversalTable[index+1]);
+            }
+
+            private void checkIndexForEntryUse() {
+                if (index < 0)
+                    throw new IllegalStateException("Entry was removed");
+            }
         }
     }
 
diff -r 554adcfb615e -r 98616c2a19c4 
test/java/util/EnumMap/DistinctEntrySetElements.java
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/EnumMap/DistinctEntrySetElements.java      Tue Mar 01 
12:55:47 2011 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* 
+ * Portions Copyright (c) 2011 IBM Corporation 
+ */
+
+/*
+ * @test
+ * @bug 6312706
+ * @summary Sets from Map.entrySet() return distinct objects for each Entry
+ * @author Neil Richards <neil.richa...@ngmr.net>, <neil_richa...@uk.ibm.com>
+ */
+
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class DistinctEntrySetElements {
+    static enum TestEnum { e00, e01, e02 }
+
+    public static void main(String[] args) throws Exception {
+        final EnumMap<TestEnum, String> enumMap = new 
EnumMap<>(TestEnum.class);
+
+        for (TestEnum e : TestEnum.values()) {
+            enumMap.put(e, e.name());
+        }
+
+        Set<Map.Entry<TestEnum, String>> entrySet = enumMap.entrySet();
+        HashSet<Map.Entry<TestEnum, String>> hashSet = new HashSet<>(entrySet);
+
+        if (false == hashSet.equals(entrySet)) {
+            throw new RuntimeException("Test FAILED: Sets are not equal.");
+        }
+        if (hashSet.hashCode() != entrySet.hashCode()) {
+            throw new RuntimeException("Test FAILED: Set's hashcodes are not 
equal.");
+        }
+    }
+}
diff -r 554adcfb615e -r 98616c2a19c4 
test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java        
Tue Mar 01 12:55:47 2011 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* 
+ * Portions Copyright (c) 2011 IBM Corporation 
+ */
+
+/*
+ * @test
+ * @bug 6312706
+ * @summary Iterator.remove() from Map.entrySet().iterator() invalidates 
returned Entry.
+ * @author Neil Richards <neil.richa...@ngmr.net>, <neil_richa...@uk.ibm.com>
+ */
+
+import java.util.EnumMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class EntrySetIteratorRemoveInvalidatesEntry {
+    static enum TestEnum { e00, e01, e02 }
+
+    public static void main(String[] args) throws Exception {
+        final EnumMap<TestEnum, String> enumMap = new 
EnumMap<>(TestEnum.class);
+
+        for (TestEnum e : TestEnum.values()) {
+            enumMap.put(e, e.name());
+        }
+
+        Iterator<Map.Entry<TestEnum, String>> entrySetIterator = 
+            enumMap.entrySet().iterator();
+        Map.Entry<TestEnum, String> entry = entrySetIterator.next();
+
+        entrySetIterator.remove();
+
+        try {
+            entry.getKey();
+            throw new RuntimeException("Test FAILED: Entry not invalidated by 
removal.");
+        } catch (Exception e) { }
+    }
+}
diff -r 554adcfb615e -r 98616c2a19c4 
test/java/util/EnumMap/SimpleSerialization.java
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/EnumMap/SimpleSerialization.java   Tue Mar 01 12:55:47 
2011 +0000
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Portions Copyright (c) 2011 IBM Corporation
+ */
+
+/*
+ * @test
+ * @bug 6312706
+ * @summary A serialized EnumMap can be successfully de-serialized.
+ * @author Neil Richards <neil.richa...@ngmr.net>, <neil_richa...@uk.ibm.com>
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.EnumMap;
+
+public class SimpleSerialization {
+    private enum TestEnum { e00, e01, e02, e03, e04, e05, e06, e07 }
+    public static void main(final String[] args) throws Exception {
+        final EnumMap<TestEnum, String> enumMap = new 
EnumMap<>(TestEnum.class);
+
+        enumMap.put(TestEnum.e01, TestEnum.e01.name());
+        enumMap.put(TestEnum.e04, TestEnum.e04.name());
+        enumMap.put(TestEnum.e05, TestEnum.e05.name());
+
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        final ObjectOutputStream oos = new ObjectOutputStream(baos);
+
+        oos.writeObject(enumMap);
+        oos.close();
+
+        final byte[] data = baos.toByteArray();
+        final ByteArrayInputStream bais = new ByteArrayInputStream(data);
+        final ObjectInputStream ois = new ObjectInputStream(bais);
+
+        final Object deserializedObject = ois.readObject();
+        ois.close();
+
+        if (false == enumMap.equals(deserializedObject)) {
+            throw new RuntimeException(getFailureText(enumMap, 
deserializedObject));
+        }
+    }
+
+    private static String getFailureText(final Object orig, final Object copy) 
{
+        final StringWriter sw = new StringWriter();
+        final PrintWriter pw = new PrintWriter(sw);
+
+        pw.println("Test FAILED: Deserialized object is not equal to the 
original object");
+        pw.print("\tOriginal: ");
+        printObject(pw, orig).println();
+        pw.print("\tCopy:     ");
+        printObject(pw, copy).println();
+
+        pw.close();
+        return sw.toString();
+    }
+
+    private static PrintWriter printObject(final PrintWriter pw, final Object 
o) {
+        pw.printf("%s@%08x", o.getClass().getName(), 
System.identityHashCode(o));
+        return pw;
+    }
+}
diff -r 554adcfb615e -r 98616c2a19c4 
test/java/util/IdentityHashMap/DistinctEntrySetElements.java
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/IdentityHashMap/DistinctEntrySetElements.java      Tue Mar 
01 12:55:47 2011 +0000
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* 
+ * Portions Copyright (c) 2011 IBM Corporation 
+ */
+
+/*
+ * @test
+ * @bug 6312706
+ * @summary Sets from Map.entrySet() return distinct objects for each Entry
+ * @author Neil Richards <neil.richa...@ngmr.net>, <neil_richa...@uk.ibm.com>
+ */
+
+import java.util.IdentityHashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class DistinctEntrySetElements {
+    public static void main(String[] args) throws Exception {
+        final IdentityHashMap<String, String> identityHashMap = 
+            new IdentityHashMap<>();
+
+        identityHashMap.put("One", "Un");
+        identityHashMap.put("Two", "Deux");
+        identityHashMap.put("Three", "Trois");
+
+        Set<Map.Entry<String, String>> entrySet = identityHashMap.entrySet();
+        HashSet<Map.Entry<String, String>> hashSet = new HashSet<>(entrySet);
+
+        // NB: These comparisons are valid in this case because none of the
+        //     keys put into 'identityHashMap' above are equal to any other.
+        if (false == hashSet.equals(entrySet)) {
+            throw new RuntimeException("Test FAILED: Sets are not equal.");
+        }
+        if (hashSet.hashCode() != entrySet.hashCode()) {
+            throw new RuntimeException("Test FAILED: Set's hashcodes are not 
equal.");
+        }
+    }
+}
diff -r 554adcfb615e -r 98616c2a19c4 
test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ 
b/test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java    
    Tue Mar 01 12:55:47 2011 +0000
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* 
+ * Portions Copyright (c) 2011 IBM Corporation 
+ */
+
+/*
+ * @test
+ * @bug 6312706
+ * @summary Iterator.remove() from Map.entrySet().iterator() invalidates 
returned Entry.
+ * @author Neil Richards <neil.richa...@ngmr.net>, <neil_richa...@uk.ibm.com>
+ */
+
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class EntrySetIteratorRemoveInvalidatesEntry {
+    public static void main(String[] args) throws Exception {
+        final IdentityHashMap<String, String> identityHashMap = 
+            new IdentityHashMap<>();
+
+        identityHashMap.put("One", "Un");
+        identityHashMap.put("Two", "Deux");
+        identityHashMap.put("Three", "Trois");
+
+        Iterator<Map.Entry<String, String>> entrySetIterator = 
+            identityHashMap.entrySet().iterator();
+        Map.Entry<String, String> entry = entrySetIterator.next();
+
+        entrySetIterator.remove();
+
+        try {
+            entry.getKey();
+            throw new RuntimeException("Test FAILED: Entry not invalidated by 
removal.");
+        } catch (Exception e) { }
+    }
+}
diff -r 554adcfb615e -r 98616c2a19c4 
test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java 
Tue Mar 01 12:55:47 2011 +0000
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* 
+ * Portions Copyright (c) 2011 IBM Corporation 
+ */
+
+/*
+ * @test
+ * @bug 6312706
+ * @summary Sets from Map.entrySet() return distinct objects for each Entry
+ * @author Neil Richards <neil.richa...@ngmr.net>, <neil_richa...@uk.ibm.com>
+ */
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class DistinctEntrySetElements {
+    public static void main(String[] args) throws Exception {
+        final ConcurrentHashMap<String, String> concurrentHashMap = 
+            new ConcurrentHashMap<>();
+
+        concurrentHashMap.put("One", "Un");
+        concurrentHashMap.put("Two", "Deux");
+        concurrentHashMap.put("Three", "Trois");
+
+        Set<Map.Entry<String, String>> entrySet = concurrentHashMap.entrySet();
+        HashSet<Map.Entry<String, String>> hashSet = new HashSet<>(entrySet);
+
+        if (false == hashSet.equals(entrySet)) {
+            throw new RuntimeException("Test FAILED: Sets are not equal.");
+        }
+        if (hashSet.hashCode() != entrySet.hashCode()) {
+            throw new RuntimeException("Test FAILED: Set's hashcodes are not 
equal.");
+        }
+    }
+}

Reply via email to