I've started to run the standard tests on this patch to make sure everything works as expected.
I will try to have any other review notes by early next week. Thanks, Mike On Mar 23 2011, at 17:51 , Neil Richards wrote: > 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 <[email protected]>, <[email protected]> > # 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: <[email protected]> > > 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 <[email protected]>, <[email protected]> > + */ > + > +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 <[email protected]>, <[email protected]> > + */ > + > +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 <[email protected]>, <[email protected]> > + */ > + > +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 <[email protected]>, <[email protected]> > + */ > + > +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 <[email protected]>, <[email protected]> > + */ > + > +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 <[email protected]>, <[email protected]> > + */ > + > +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."); > + } > + } > +} >
