United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-7020923 Unexpected behavior of EnumMap.entrySet()
JDK-7020923 : Unexpected behavior of EnumMap.entrySet()

Details
Type:
Bug
Submit Date:
2011-02-20
Status:
Closed
Updated Date:
2012-07-13
Project Name:
JDK
Resolved Date:
2011-04-04
Component:
core-libs
OS:
windows_xp
Sub-Component:
java.util
CPU:
x86
Priority:
P4
Resolution:
Duplicate
Affected Versions:
6u23
Fixed Versions:
7

Related Reports
Duplicate:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) Client VM (build 19.1-b02, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
The entries returned by the Iterator of EntryMap.entrySet() DO NOT preserve values during iteration.

If Iterator.next() is called then the Entry obtained by the previous call to next() will unexpectedly change its key and value to the "next" element in the EnumMap.

This contradicts with http://download.oracle.com/javase/6/docs/api/java/util/EnumMap.html#entrySet() which says that entrySet() obeys the general contract of Map.keySet()
and
http://download.oracle.com/javase/6/docs/api/java/util/Map.Entry.html which says " Map.Entry objects are valid only for the duration of the iteration; more formally, the behavior of a map entry is undefined if the backing map has been modified after the entry was returned by the iterator"

The actual behavior is that the key and value inside the Entry are changed implicitly DURING the iteration (while the backing map is NOT modified)

The Set<Map.Entry> returned by the HashMap.entrySet() does not have this issue

The cause of the issue is that java.util.EnumMap.EntryIterator.next() returns the iterator itself. The javadoc for the class EntryIterator simply says "Since we don't use Entry objects, we use the Iterator itself as entry" so it's hard to understand why such implementation has been taken.


The solutions suggested by me are:
 - (Preferrable) next() should create the freshly created Entry object instead of the iterator
 - The javadocs of the EnumMap are updated to explicitly say that Entry returned by next() method will be implicitly modified by subsequent next() calls

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute the program from the test case (see below)

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The expected output of the program should be:
FIRST : first
FIRST : first
SECOND : second
SECOND : second
ACTUAL -
The actual output of the program is
FIRST : first
SECOND : second
SECOND : second
SECOND : second

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package org.volchyn.example;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;

public enum SampleEnum {

    FIRST,
    SECOND,
    THIRD;
    
    public static void main(String[] args) {
        /* Use EnumMap to add to mappings and then iterate through this map */
        EnumMap<SampleEnum, String> enumMap = new EnumMap<SampleEnum, String>(
                SampleEnum.class);
        enumMap.put(FIRST, "first");
        enumMap.put(SECOND, "second");
        Set<Entry<SampleEnum, String>> enumEntrySet = enumMap.entrySet();
        Iterator<Entry<SampleEnum, String>> enumIterator = enumEntrySet.iterator();
        Entry<SampleEnum, String> enumEntry = enumIterator.next();
        System.out.println(enumEntry.getKey() + " : " + enumEntry.getValue());
        /* Continue iteration */
        enumIterator.next();
        /* The key and value inside the entry have been changed unexpectedly */
        System.out.println(enumEntry.getKey() + " : " + enumEntry.getValue());
        
        /* Execute the very same example using HashMap */
        HashMap<SampleEnum, String> hashMap = new HashMap<SampleEnum, String>();
        hashMap.put(FIRST, "first");
        hashMap.put(SECOND, "second");
        Set<Entry<SampleEnum, String>> hashEntrySet = hashMap.entrySet();
        Iterator<Entry<SampleEnum, String>> hashIterator = hashEntrySet.iterator();
        Entry<SampleEnum, String> hashEntry = hashIterator.next();
        System.out.println(hashEntry.getKey() + " : " + hashEntry.getValue());
        /* Continue iteration */
        hashIterator.next();
        /* The key and value inside the entry are preserverd */
        System.out.println(hashEntry.getKey() + " : " + hashEntry.getValue());
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The workaround that I used is to store the actual key and value of the Entry before calling Iterator.next()

                                    

Comments
PUBLIC COMMENTS

This is being fixed as CR # 6312706.
                                     
2011-04-04



Hardware and Software, Engineered to Work Together