Request for review: 6312706: Map entrySet iterators should return different entries on each call to next()
Neil Richards
neil.richards at ngmr.net
Sat Mar 26 08:10:09 UTC 2011
Hi Mike - thanks for posting up the webrev, and for your review.
On Fri, 2011-03-25 at 19:33 -0700, Mike Duigou wrote:
> I have run the jtreg regression suite against your webrev. I had to
> make one change to IdentityHashMap to satisfy the Collection/MOAT
> test. The failure condition involved calling EntrySet.remove() when
> there was no current entry due to a prior remove() or because next()
> had never been called.
Oh, I'm sorry for this slip - my intention was that the assignment of
'lastReturnedIndex' at the start of remove() should be identical to that
done (correctly) in the corresponding place in the changes in EnumMap,
namely:
lastReturnedIndex =
((null == lastReturnedEntry) ? -1 :
lastReturnedEntry.index);
which allows the checking which is done inside the subsequent call to
super.remove() to throw the appropriate exception if lastReturnedEntry
is null.
(With the new code, the value of the lastReturnedIndex field inside an
EntryIterator object is really only of any significance within remove(),
once it has been assigned by the line above, in preparation for the call
to super.remove().
At all other points, the last returned index is held in
lastReturnedEntry.index, or by lastReturnedEntry being null).
With the assignment line suitably corrected, I believe there is no need
for the added check.
Please find below a corrected changeset 'hg export' with the relevant
correction made.
> If you have any metrics that you can contribute which show this change
> to have negligible impact upon performance across common benchmarks it
> would definitely help to allay any fears.
Our versions of Java 6 (which have been "out in the field" for more than
3 years now) are not susceptible to this particular problem, so our
performance folk (yes, we have them too!) have obviously found its
resolution to be unconcerning.
Whilst developing the suggested change, I also reviewed EnumMap and
IdentityHashMap for opportunities to avoid using entry sets (and their
iterators) for the current object altogether.
This resulted in the expanded methods I highlighted in my previous post.
(In these cases, the performance should actually improve slightly, as
using the expansion avoids at least two object creations).
Do you have any particular "common benchmarks" in mind?
I'm currently out of office for a few days (visiting family in Concord),
but can look to work on provide supporting data once I'm back "in situ",
as necessary.
Hope this helps,
Neil
--
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.richards at ngmr.net>, <neil_richards at uk.ibm.com>
# Date 1298984147 0
# Branch j6312706
# Node ID bc07c38bac7cbd7ca47bbc382557831ec5c2100c
# 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.richards at ngmr.net>
diff --git a/src/share/classes/java/util/EnumMap.java b/src/share/classes/java/util/EnumMap.java
--- a/src/share/classes/java/util/EnumMap.java
+++ b/src/share/classes/java/util/EnumMap.java
@@ -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 --git a/src/share/classes/java/util/IdentityHashMap.java b/src/share/classes/java/util/IdentityHashMap.java
--- a/src/share/classes/java/util/IdentityHashMap.java
+++ b/src/share/classes/java/util/IdentityHashMap.java
@@ -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 =
+ ((null == lastReturnedEntry) ? -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 --git a/test/java/util/EnumMap/DistinctEntrySetElements.java b/test/java/util/EnumMap/DistinctEntrySetElements.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/EnumMap/DistinctEntrySetElements.java
@@ -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.richards at ngmr.net>, <neil_richards at 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 --git a/test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java b/test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/EnumMap/EntrySetIteratorRemoveInvalidatesEntry.java
@@ -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.richards at ngmr.net>, <neil_richards at 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 --git a/test/java/util/EnumMap/SimpleSerialization.java b/test/java/util/EnumMap/SimpleSerialization.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/EnumMap/SimpleSerialization.java
@@ -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.richards at ngmr.net>, <neil_richards at 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 --git a/test/java/util/IdentityHashMap/DistinctEntrySetElements.java b/test/java/util/IdentityHashMap/DistinctEntrySetElements.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/IdentityHashMap/DistinctEntrySetElements.java
@@ -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.richards at ngmr.net>, <neil_richards at 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 --git a/test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java b/test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/IdentityHashMap/EntrySetIteratorRemoveInvalidatesEntry.java
@@ -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.richards at ngmr.net>, <neil_richards at 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 --git a/test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java b/test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/concurrent/ConcurrentHashMap/DistinctEntrySetElements.java
@@ -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.richards at ngmr.net>, <neil_richards at 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.");
+ }
+ }
+}
More information about the core-libs-dev
mailing list