Request for review: 6312706: Map entrySet iterators should return different entries on each call to next()
Mike Duigou
mike.duigou at oracle.com
Fri Mar 25 18:37:57 UTC 2011
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 <neil.richards at ngmr.net>, <neil_richards at 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.richards at 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.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 -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.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 -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.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 -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.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 -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.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 -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.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