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