src/share/classes/java/util/EnumMap.java
Print this page
rev 3765 : 6312706: Map entrySet iterators should return different entries on each call to next()
Reviewed-by: mduigou
Contributed-by: Neil Richards <neil.richards@ngmr.net>
@@ -104,21 +104,25 @@
private transient int size = 0;
/**
* 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);
}
private V unmaskNull(Object value) {
return (V) (value == NULL ? null : value);
}
- private static Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0];
+ private static final Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0];
/**
* Creates an empty enum map with the specified key type.
*
* @param keyType the class object of the key type for this enum map
@@ -462,10 +466,11 @@
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
+
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry entry = (Map.Entry)o;
return containsMapping(entry.getKey(), entry.getValue());
@@ -550,76 +555,88 @@
lastReturnedIndex = index++;
return unmaskNull(vals[lastReturnedIndex]);
}
}
- /**
- * 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 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;
+
+ private Entry(int index) {
+ this.index = index;
}
public K getKey() {
- checkLastReturnedIndexForEntryUse();
- return keyUniverse[lastReturnedIndex];
+ checkIndexForEntryUse();
+ return keyUniverse[index];
}
public V getValue() {
- checkLastReturnedIndexForEntryUse();
- return unmaskNull(vals[lastReturnedIndex]);
+ checkIndexForEntryUse();
+ return unmaskNull(vals[index]);
}
public V setValue(V value) {
- checkLastReturnedIndexForEntryUse();
- V oldValue = unmaskNull(vals[lastReturnedIndex]);
- vals[lastReturnedIndex] = maskNull(value);
+ checkIndexForEntryUse();
+ V oldValue = unmaskNull(vals[index]);
+ vals[index] = maskNull(value);
return oldValue;
}
public boolean equals(Object o) {
- if (lastReturnedIndex < 0)
+ if (index < 0)
return o == this;
if (!(o instanceof Map.Entry))
return false;
+
Map.Entry e = (Map.Entry)o;
- V ourValue = unmaskNull(vals[lastReturnedIndex]);
+ V ourValue = unmaskNull(vals[index]);
Object hisValue = e.getValue();
- return e.getKey() == keyUniverse[lastReturnedIndex] &&
+ return (e.getKey() == keyUniverse[index] &&
(ourValue == hisValue ||
- (ourValue != null && ourValue.equals(hisValue)));
+ (ourValue != null && ourValue.equals(hisValue))));
}
public int hashCode() {
- if (lastReturnedIndex < 0)
+ if (index < 0)
return super.hashCode();
- Object value = vals[lastReturnedIndex];
- return keyUniverse[lastReturnedIndex].hashCode()
- ^ (value == NULL ? 0 : value.hashCode());
+ return entryHashCode(index);
}
public String toString() {
- if (lastReturnedIndex < 0)
+ if (index < 0)
return super.toString();
- return keyUniverse[lastReturnedIndex] + "="
- + unmaskNull(vals[lastReturnedIndex]);
+ return keyUniverse[index] + "="
+ + unmaskNull(vals[index]);
}
- private void checkLastReturnedIndexForEntryUse() {
- if (lastReturnedIndex < 0)
+ private void checkIndexForEntryUse() {
+ if (index < 0)
throw new IllegalStateException("Entry was removed");
}
}
+ }
// Comparison and hashing
/**
* Compares the specified object with this map for equality. Returns
@@ -629,14 +646,39 @@
*
* @param o the object to be compared for equality with this map
* @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;
- EnumMap em = (EnumMap)o;
+ Map<K,V> m = (Map<K,V>)o;
+ if (size != m.size())
+ return false;
+
+ 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;
// Key types match, compare each value
for (int i = 0; i < keyUniverse.length; i++) {
@@ -648,10 +690,30 @@
}
return true;
}
/**
+ * 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.
*
* @return a shallow copy of this enum map
*/
@@ -703,13 +765,17 @@
// Write out size (number of Mappings)
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--;
+ }
}
}
/**
* Reconstitute the <tt>EnumMap</tt> instance from a stream (i.e.,