jtulach@1292: /* jtulach@1292: * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. jtulach@1292: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jtulach@1292: * jtulach@1292: * This code is free software; you can redistribute it and/or modify it jtulach@1292: * under the terms of the GNU General Public License version 2 only, as jtulach@1292: * published by the Free Software Foundation. Oracle designates this jtulach@1292: * particular file as subject to the "Classpath" exception as provided jtulach@1292: * by Oracle in the LICENSE file that accompanied this code. jtulach@1292: * jtulach@1292: * This code is distributed in the hope that it will be useful, but WITHOUT jtulach@1292: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jtulach@1292: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jtulach@1292: * version 2 for more details (a copy is included in the LICENSE file that jtulach@1292: * accompanied this code). jtulach@1292: * jtulach@1292: * You should have received a copy of the GNU General Public License version jtulach@1292: * 2 along with this work; if not, write to the Free Software Foundation, jtulach@1292: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jtulach@1292: * jtulach@1292: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jtulach@1292: * or visit www.oracle.com if you need additional information or have any jtulach@1292: * questions. jtulach@1292: */ jtulach@1292: jtulach@1292: package java.util; jtulach@1292: jtulach@1292: import java.util.Map.Entry; jtulach@1292: import sun.misc.SharedSecrets; jtulach@1292: jtulach@1292: /** jtulach@1292: * A specialized {@link Map} implementation for use with enum type keys. All jtulach@1292: * of the keys in an enum map must come from a single enum type that is jtulach@1292: * specified, explicitly or implicitly, when the map is created. Enum maps jtulach@1292: * are represented internally as arrays. This representation is extremely jtulach@1292: * compact and efficient. jtulach@1292: * jtulach@1292: *

Enum maps are maintained in the natural order of their keys jtulach@1292: * (the order in which the enum constants are declared). This is reflected jtulach@1292: * in the iterators returned by the collections views ({@link #keySet()}, jtulach@1292: * {@link #entrySet()}, and {@link #values()}). jtulach@1292: * jtulach@1292: *

Iterators returned by the collection views are weakly consistent: jtulach@1292: * they will never throw {@link ConcurrentModificationException} and they may jtulach@1292: * or may not show the effects of any modifications to the map that occur while jtulach@1292: * the iteration is in progress. jtulach@1292: * jtulach@1292: *

Null keys are not permitted. Attempts to insert a null key will jtulach@1292: * throw {@link NullPointerException}. Attempts to test for the jtulach@1292: * presence of a null key or to remove one will, however, function properly. jtulach@1292: * Null values are permitted. jtulach@1292: jtulach@1292: *

Like most collection implementations EnumMap is not jtulach@1292: * synchronized. If multiple threads access an enum map concurrently, and at jtulach@1292: * least one of the threads modifies the map, it should be synchronized jtulach@1292: * externally. This is typically accomplished by synchronizing on some jtulach@1292: * object that naturally encapsulates the enum map. If no such object exists, jtulach@1292: * the map should be "wrapped" using the {@link Collections#synchronizedMap} jtulach@1292: * method. This is best done at creation time, to prevent accidental jtulach@1292: * unsynchronized access: jtulach@1292: * jtulach@1292: *

jtulach@1292:  *     Map<EnumKey, V> m
jtulach@1292:  *         = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...));
jtulach@1292:  * 
jtulach@1292: * jtulach@1292: *

Implementation note: All basic operations execute in constant time. jtulach@1292: * They are likely (though not guaranteed) to be faster than their jtulach@1292: * {@link HashMap} counterparts. jtulach@1292: * jtulach@1292: *

This class is a member of the jtulach@1292: * jtulach@1292: * Java Collections Framework. jtulach@1292: * jtulach@1292: * @author Josh Bloch jtulach@1292: * @see EnumSet jtulach@1292: * @since 1.5 jtulach@1292: */ jtulach@1292: public class EnumMap, V> extends AbstractMap jtulach@1292: implements java.io.Serializable, Cloneable jtulach@1292: { jtulach@1292: /** jtulach@1292: * The Class object for the enum type of all the keys of this map. jtulach@1292: * jtulach@1292: * @serial jtulach@1292: */ jtulach@1292: private final Class keyType; jtulach@1292: jtulach@1292: /** jtulach@1292: * All of the values comprising K. (Cached for performance.) jtulach@1292: */ jtulach@1292: private transient K[] keyUniverse; jtulach@1292: jtulach@1292: /** jtulach@1292: * Array representation of this map. The ith element is the value jtulach@1292: * to which universe[i] is currently mapped, or null if it isn't jtulach@1292: * mapped to anything, or NULL if it's mapped to null. jtulach@1292: */ jtulach@1292: private transient Object[] vals; jtulach@1292: jtulach@1292: /** jtulach@1292: * The number of mappings in this map. jtulach@1292: */ jtulach@1292: private transient int size = 0; jtulach@1292: jtulach@1292: /** jtulach@1292: * Distinguished non-null value for representing null values. jtulach@1292: */ jtulach@1292: private static final Object NULL = new Integer(0); jtulach@1292: jtulach@1292: private Object maskNull(Object value) { jtulach@1292: return (value == null ? NULL : value); jtulach@1292: } jtulach@1292: jtulach@1292: private V unmaskNull(Object value) { jtulach@1292: return (V) (value == NULL ? null : value); jtulach@1292: } jtulach@1292: jtulach@1292: private static final Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0]; jtulach@1292: jtulach@1292: /** jtulach@1292: * Creates an empty enum map with the specified key type. jtulach@1292: * jtulach@1292: * @param keyType the class object of the key type for this enum map jtulach@1292: * @throws NullPointerException if keyType is null jtulach@1292: */ jtulach@1292: public EnumMap(Class keyType) { jtulach@1292: this.keyType = keyType; jtulach@1292: keyUniverse = getKeyUniverse(keyType); jtulach@1292: vals = new Object[keyUniverse.length]; jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Creates an enum map with the same key type as the specified enum jtulach@1292: * map, initially containing the same mappings (if any). jtulach@1292: * jtulach@1292: * @param m the enum map from which to initialize this enum map jtulach@1292: * @throws NullPointerException if m is null jtulach@1292: */ jtulach@1292: public EnumMap(EnumMap m) { jtulach@1292: keyType = m.keyType; jtulach@1292: keyUniverse = m.keyUniverse; jtulach@1292: vals = m.vals.clone(); jtulach@1292: size = m.size; jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Creates an enum map initialized from the specified map. If the jtulach@1292: * specified map is an EnumMap instance, this constructor behaves jtulach@1292: * identically to {@link #EnumMap(EnumMap)}. Otherwise, the specified map jtulach@1292: * must contain at least one mapping (in order to determine the new jtulach@1292: * enum map's key type). jtulach@1292: * jtulach@1292: * @param m the map from which to initialize this enum map jtulach@1292: * @throws IllegalArgumentException if m is not an jtulach@1292: * EnumMap instance and contains no mappings jtulach@1292: * @throws NullPointerException if m is null jtulach@1292: */ jtulach@1292: public EnumMap(Map m) { jtulach@1292: if (m instanceof EnumMap) { jtulach@1292: EnumMap em = (EnumMap) m; jtulach@1292: keyType = em.keyType; jtulach@1292: keyUniverse = em.keyUniverse; jtulach@1292: vals = em.vals.clone(); jtulach@1292: size = em.size; jtulach@1292: } else { jtulach@1292: if (m.isEmpty()) jtulach@1292: throw new IllegalArgumentException("Specified map is empty"); jtulach@1292: keyType = m.keySet().iterator().next().getDeclaringClass(); jtulach@1292: keyUniverse = getKeyUniverse(keyType); jtulach@1292: vals = new Object[keyUniverse.length]; jtulach@1292: putAll(m); jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: // Query Operations jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns the number of key-value mappings in this map. jtulach@1292: * jtulach@1292: * @return the number of key-value mappings in this map jtulach@1292: */ jtulach@1292: public int size() { jtulach@1292: return size; jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns true if this map maps one or more keys to the jtulach@1292: * specified value. jtulach@1292: * jtulach@1292: * @param value the value whose presence in this map is to be tested jtulach@1292: * @return true if this map maps one or more keys to this value jtulach@1292: */ jtulach@1292: public boolean containsValue(Object value) { jtulach@1292: value = maskNull(value); jtulach@1292: jtulach@1292: for (Object val : vals) jtulach@1292: if (value.equals(val)) jtulach@1292: return true; jtulach@1292: jtulach@1292: return false; jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns true if this map contains a mapping for the specified jtulach@1292: * key. jtulach@1292: * jtulach@1292: * @param key the key whose presence in this map is to be tested jtulach@1292: * @return true if this map contains a mapping for the specified jtulach@1292: * key jtulach@1292: */ jtulach@1292: public boolean containsKey(Object key) { jtulach@1292: return isValidKey(key) && vals[((Enum)key).ordinal()] != null; jtulach@1292: } jtulach@1292: jtulach@1292: private boolean containsMapping(Object key, Object value) { jtulach@1292: return isValidKey(key) && jtulach@1292: maskNull(value).equals(vals[((Enum)key).ordinal()]); jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns the value to which the specified key is mapped, jtulach@1292: * or {@code null} if this map contains no mapping for the key. jtulach@1292: * jtulach@1292: *

More formally, if this map contains a mapping from a key jtulach@1292: * {@code k} to a value {@code v} such that {@code (key == k)}, jtulach@1292: * then this method returns {@code v}; otherwise it returns jtulach@1292: * {@code null}. (There can be at most one such mapping.) jtulach@1292: * jtulach@1292: *

A return value of {@code null} does not necessarily jtulach@1292: * indicate that the map contains no mapping for the key; it's also jtulach@1292: * possible that the map explicitly maps the key to {@code null}. jtulach@1292: * The {@link #containsKey containsKey} operation may be used to jtulach@1292: * distinguish these two cases. jtulach@1292: */ jtulach@1292: public V get(Object key) { jtulach@1292: return (isValidKey(key) ? jtulach@1292: unmaskNull(vals[((Enum)key).ordinal()]) : null); jtulach@1292: } jtulach@1292: jtulach@1292: // Modification Operations jtulach@1292: jtulach@1292: /** jtulach@1292: * Associates the specified value with the specified key in this map. jtulach@1292: * If the map previously contained a mapping for this key, the old jtulach@1292: * value is replaced. jtulach@1292: * jtulach@1292: * @param key the key with which the specified value is to be associated jtulach@1292: * @param value the value to be associated with the specified key jtulach@1292: * jtulach@1292: * @return the previous value associated with specified key, or jtulach@1292: * null if there was no mapping for key. (A null jtulach@1292: * return can also indicate that the map previously associated jtulach@1292: * null with the specified key.) jtulach@1292: * @throws NullPointerException if the specified key is null jtulach@1292: */ jtulach@1292: public V put(K key, V value) { jtulach@1292: typeCheck(key); jtulach@1292: jtulach@1292: int index = key.ordinal(); jtulach@1292: Object oldValue = vals[index]; jtulach@1292: vals[index] = maskNull(value); jtulach@1292: if (oldValue == null) jtulach@1292: size++; jtulach@1292: return unmaskNull(oldValue); jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Removes the mapping for this key from this map if present. jtulach@1292: * jtulach@1292: * @param key the key whose mapping is to be removed from the map jtulach@1292: * @return the previous value associated with specified key, or jtulach@1292: * null if there was no entry for key. (A null jtulach@1292: * return can also indicate that the map previously associated jtulach@1292: * null with the specified key.) jtulach@1292: */ jtulach@1292: public V remove(Object key) { jtulach@1292: if (!isValidKey(key)) jtulach@1292: return null; jtulach@1292: int index = ((Enum)key).ordinal(); jtulach@1292: Object oldValue = vals[index]; jtulach@1292: vals[index] = null; jtulach@1292: if (oldValue != null) jtulach@1292: size--; jtulach@1292: return unmaskNull(oldValue); jtulach@1292: } jtulach@1292: jtulach@1292: private boolean removeMapping(Object key, Object value) { jtulach@1292: if (!isValidKey(key)) jtulach@1292: return false; jtulach@1292: int index = ((Enum)key).ordinal(); jtulach@1292: if (maskNull(value).equals(vals[index])) { jtulach@1292: vals[index] = null; jtulach@1292: size--; jtulach@1292: return true; jtulach@1292: } jtulach@1292: return false; jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns true if key is of the proper type to be a key in this jtulach@1292: * enum map. jtulach@1292: */ jtulach@1292: private boolean isValidKey(Object key) { jtulach@1292: if (key == null) jtulach@1292: return false; jtulach@1292: jtulach@1292: // Cheaper than instanceof Enum followed by getDeclaringClass jtulach@1292: Class keyClass = key.getClass(); jtulach@1292: return keyClass == keyType || keyClass.getSuperclass() == keyType; jtulach@1292: } jtulach@1292: jtulach@1292: // Bulk Operations jtulach@1292: jtulach@1292: /** jtulach@1292: * Copies all of the mappings from the specified map to this map. jtulach@1292: * These mappings will replace any mappings that this map had for jtulach@1292: * any of the keys currently in the specified map. jtulach@1292: * jtulach@1292: * @param m the mappings to be stored in this map jtulach@1292: * @throws NullPointerException the specified map is null, or if jtulach@1292: * one or more keys in the specified map are null jtulach@1292: */ jtulach@1292: public void putAll(Map m) { jtulach@1292: if (m instanceof EnumMap) { jtulach@1292: EnumMap em = jtulach@1292: (EnumMap)m; jtulach@1292: if (em.keyType != keyType) { jtulach@1292: if (em.isEmpty()) jtulach@1292: return; jtulach@1292: throw new ClassCastException(em.keyType + " != " + keyType); jtulach@1292: } jtulach@1292: jtulach@1292: for (int i = 0; i < keyUniverse.length; i++) { jtulach@1292: Object emValue = em.vals[i]; jtulach@1292: if (emValue != null) { jtulach@1292: if (vals[i] == null) jtulach@1292: size++; jtulach@1292: vals[i] = emValue; jtulach@1292: } jtulach@1292: } jtulach@1292: } else { jtulach@1292: super.putAll(m); jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Removes all mappings from this map. jtulach@1292: */ jtulach@1292: public void clear() { jtulach@1292: Arrays.fill(vals, null); jtulach@1292: size = 0; jtulach@1292: } jtulach@1292: jtulach@1292: // Views jtulach@1292: jtulach@1292: /** jtulach@1292: * This field is initialized to contain an instance of the entry set jtulach@1292: * view the first time this view is requested. The view is stateless, jtulach@1292: * so there's no reason to create more than one. jtulach@1292: */ jtulach@1292: private transient Set> entrySet = null; jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns a {@link Set} view of the keys contained in this map. jtulach@1292: * The returned set obeys the general contract outlined in jtulach@1292: * {@link Map#keySet()}. The set's iterator will return the keys jtulach@1292: * in their natural order (the order in which the enum constants jtulach@1292: * are declared). jtulach@1292: * jtulach@1292: * @return a set view of the keys contained in this enum map jtulach@1292: */ jtulach@1292: public Set keySet() { jtulach@1292: Set ks = keySet; jtulach@1292: if (ks != null) jtulach@1292: return ks; jtulach@1292: else jtulach@1292: return keySet = new KeySet(); jtulach@1292: } jtulach@1292: jtulach@1292: private class KeySet extends AbstractSet { jtulach@1292: public Iterator iterator() { jtulach@1292: return new KeyIterator(); jtulach@1292: } jtulach@1292: public int size() { jtulach@1292: return size; jtulach@1292: } jtulach@1292: public boolean contains(Object o) { jtulach@1292: return containsKey(o); jtulach@1292: } jtulach@1292: public boolean remove(Object o) { jtulach@1292: int oldSize = size; jtulach@1292: EnumMap.this.remove(o); jtulach@1292: return size != oldSize; jtulach@1292: } jtulach@1292: public void clear() { jtulach@1292: EnumMap.this.clear(); jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns a {@link Collection} view of the values contained in this map. jtulach@1292: * The returned collection obeys the general contract outlined in jtulach@1292: * {@link Map#values()}. The collection's iterator will return the jtulach@1292: * values in the order their corresponding keys appear in map, jtulach@1292: * which is their natural order (the order in which the enum constants jtulach@1292: * are declared). jtulach@1292: * jtulach@1292: * @return a collection view of the values contained in this map jtulach@1292: */ jtulach@1292: public Collection values() { jtulach@1292: Collection vs = values; jtulach@1292: if (vs != null) jtulach@1292: return vs; jtulach@1292: else jtulach@1292: return values = new Values(); jtulach@1292: } jtulach@1292: jtulach@1292: private class Values extends AbstractCollection { jtulach@1292: public Iterator iterator() { jtulach@1292: return new ValueIterator(); jtulach@1292: } jtulach@1292: public int size() { jtulach@1292: return size; jtulach@1292: } jtulach@1292: public boolean contains(Object o) { jtulach@1292: return containsValue(o); jtulach@1292: } jtulach@1292: public boolean remove(Object o) { jtulach@1292: o = maskNull(o); jtulach@1292: jtulach@1292: for (int i = 0; i < vals.length; i++) { jtulach@1292: if (o.equals(vals[i])) { jtulach@1292: vals[i] = null; jtulach@1292: size--; jtulach@1292: return true; jtulach@1292: } jtulach@1292: } jtulach@1292: return false; jtulach@1292: } jtulach@1292: public void clear() { jtulach@1292: EnumMap.this.clear(); jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns a {@link Set} view of the mappings contained in this map. jtulach@1292: * The returned set obeys the general contract outlined in jtulach@1292: * {@link Map#keySet()}. The set's iterator will return the jtulach@1292: * mappings in the order their keys appear in map, which is their jtulach@1292: * natural order (the order in which the enum constants are declared). jtulach@1292: * jtulach@1292: * @return a set view of the mappings contained in this enum map jtulach@1292: */ jtulach@1292: public Set> entrySet() { jtulach@1292: Set> es = entrySet; jtulach@1292: if (es != null) jtulach@1292: return es; jtulach@1292: else jtulach@1292: return entrySet = new EntrySet(); jtulach@1292: } jtulach@1292: jtulach@1292: private class EntrySet extends AbstractSet> { jtulach@1292: public Iterator> iterator() { jtulach@1292: return new EntryIterator(); jtulach@1292: } jtulach@1292: jtulach@1292: public boolean contains(Object o) { jtulach@1292: if (!(o instanceof Map.Entry)) jtulach@1292: return false; jtulach@1292: Map.Entry entry = (Map.Entry)o; jtulach@1292: return containsMapping(entry.getKey(), entry.getValue()); jtulach@1292: } jtulach@1292: public boolean remove(Object o) { jtulach@1292: if (!(o instanceof Map.Entry)) jtulach@1292: return false; jtulach@1292: Map.Entry entry = (Map.Entry)o; jtulach@1292: return removeMapping(entry.getKey(), entry.getValue()); jtulach@1292: } jtulach@1292: public int size() { jtulach@1292: return size; jtulach@1292: } jtulach@1292: public void clear() { jtulach@1292: EnumMap.this.clear(); jtulach@1292: } jtulach@1292: public Object[] toArray() { jtulach@1292: return fillEntryArray(new Object[size]); jtulach@1292: } jtulach@1292: @SuppressWarnings("unchecked") jtulach@1292: public T[] toArray(T[] a) { jtulach@1292: int size = size(); jtulach@1292: if (a.length < size) jtulach@1292: a = (T[])java.lang.reflect.Array jtulach@1292: .newInstance(a.getClass().getComponentType(), size); jtulach@1292: if (a.length > size) jtulach@1292: a[size] = null; jtulach@1292: return (T[]) fillEntryArray(a); jtulach@1292: } jtulach@1292: private Object[] fillEntryArray(Object[] a) { jtulach@1292: int j = 0; jtulach@1292: for (int i = 0; i < vals.length; i++) jtulach@1292: if (vals[i] != null) jtulach@1292: a[j++] = new AbstractMap.SimpleEntry<>( jtulach@1292: keyUniverse[i], unmaskNull(vals[i])); jtulach@1292: return a; jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: private abstract class EnumMapIterator implements Iterator { jtulach@1292: // Lower bound on index of next element to return jtulach@1292: int index = 0; jtulach@1292: jtulach@1292: // Index of last returned element, or -1 if none jtulach@1292: int lastReturnedIndex = -1; jtulach@1292: jtulach@1292: public boolean hasNext() { jtulach@1292: while (index < vals.length && vals[index] == null) jtulach@1292: index++; jtulach@1292: return index != vals.length; jtulach@1292: } jtulach@1292: jtulach@1292: public void remove() { jtulach@1292: checkLastReturnedIndex(); jtulach@1292: jtulach@1292: if (vals[lastReturnedIndex] != null) { jtulach@1292: vals[lastReturnedIndex] = null; jtulach@1292: size--; jtulach@1292: } jtulach@1292: lastReturnedIndex = -1; jtulach@1292: } jtulach@1292: jtulach@1292: private void checkLastReturnedIndex() { jtulach@1292: if (lastReturnedIndex < 0) jtulach@1292: throw new IllegalStateException(); jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: private class KeyIterator extends EnumMapIterator { jtulach@1292: public K next() { jtulach@1292: if (!hasNext()) jtulach@1292: throw new NoSuchElementException(); jtulach@1292: lastReturnedIndex = index++; jtulach@1292: return keyUniverse[lastReturnedIndex]; jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: private class ValueIterator extends EnumMapIterator { jtulach@1292: public V next() { jtulach@1292: if (!hasNext()) jtulach@1292: throw new NoSuchElementException(); jtulach@1292: lastReturnedIndex = index++; jtulach@1292: return unmaskNull(vals[lastReturnedIndex]); jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: private class EntryIterator extends EnumMapIterator> { jtulach@1292: private Entry lastReturnedEntry = null; jtulach@1292: jtulach@1292: public Map.Entry next() { jtulach@1292: if (!hasNext()) jtulach@1292: throw new NoSuchElementException(); jtulach@1292: lastReturnedEntry = new Entry(index++); jtulach@1292: return lastReturnedEntry; jtulach@1292: } jtulach@1292: jtulach@1292: public void remove() { jtulach@1292: lastReturnedIndex = jtulach@1292: ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index); jtulach@1292: super.remove(); jtulach@1292: lastReturnedEntry.index = lastReturnedIndex; jtulach@1292: lastReturnedEntry = null; jtulach@1292: } jtulach@1292: jtulach@1292: private class Entry implements Map.Entry { jtulach@1292: private int index; jtulach@1292: jtulach@1292: private Entry(int index) { jtulach@1292: this.index = index; jtulach@1292: } jtulach@1292: jtulach@1292: public K getKey() { jtulach@1292: checkIndexForEntryUse(); jtulach@1292: return keyUniverse[index]; jtulach@1292: } jtulach@1292: jtulach@1292: public V getValue() { jtulach@1292: checkIndexForEntryUse(); jtulach@1292: return unmaskNull(vals[index]); jtulach@1292: } jtulach@1292: jtulach@1292: public V setValue(V value) { jtulach@1292: checkIndexForEntryUse(); jtulach@1292: V oldValue = unmaskNull(vals[index]); jtulach@1292: vals[index] = maskNull(value); jtulach@1292: return oldValue; jtulach@1292: } jtulach@1292: jtulach@1292: public boolean equals(Object o) { jtulach@1292: if (index < 0) jtulach@1292: return o == this; jtulach@1292: jtulach@1292: if (!(o instanceof Map.Entry)) jtulach@1292: return false; jtulach@1292: jtulach@1292: Map.Entry e = (Map.Entry)o; jtulach@1292: V ourValue = unmaskNull(vals[index]); jtulach@1292: Object hisValue = e.getValue(); jtulach@1292: return (e.getKey() == keyUniverse[index] && jtulach@1292: (ourValue == hisValue || jtulach@1292: (ourValue != null && ourValue.equals(hisValue)))); jtulach@1292: } jtulach@1292: jtulach@1292: public int hashCode() { jtulach@1292: if (index < 0) jtulach@1292: return super.hashCode(); jtulach@1292: jtulach@1292: return entryHashCode(index); jtulach@1292: } jtulach@1292: jtulach@1292: public String toString() { jtulach@1292: if (index < 0) jtulach@1292: return super.toString(); jtulach@1292: jtulach@1292: return keyUniverse[index] + "=" jtulach@1292: + unmaskNull(vals[index]); jtulach@1292: } jtulach@1292: jtulach@1292: private void checkIndexForEntryUse() { jtulach@1292: if (index < 0) jtulach@1292: throw new IllegalStateException("Entry was removed"); jtulach@1292: } jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: // Comparison and hashing jtulach@1292: jtulach@1292: /** jtulach@1292: * Compares the specified object with this map for equality. Returns jtulach@1292: * true if the given object is also a map and the two maps jtulach@1292: * represent the same mappings, as specified in the {@link jtulach@1292: * Map#equals(Object)} contract. jtulach@1292: * jtulach@1292: * @param o the object to be compared for equality with this map jtulach@1292: * @return true if the specified object is equal to this map jtulach@1292: */ jtulach@1292: public boolean equals(Object o) { jtulach@1292: if (this == o) jtulach@1292: return true; jtulach@1292: if (o instanceof EnumMap) jtulach@1292: return equals((EnumMap)o); jtulach@1292: if (!(o instanceof Map)) jtulach@1292: return false; jtulach@1292: jtulach@1292: Map m = (Map)o; jtulach@1292: if (size != m.size()) jtulach@1292: return false; jtulach@1292: jtulach@1292: for (int i = 0; i < keyUniverse.length; i++) { jtulach@1292: if (null != vals[i]) { jtulach@1292: K key = keyUniverse[i]; jtulach@1292: V value = unmaskNull(vals[i]); jtulach@1292: if (null == value) { jtulach@1292: if (!((null == m.get(key)) && m.containsKey(key))) jtulach@1292: return false; jtulach@1292: } else { jtulach@1292: if (!value.equals(m.get(key))) jtulach@1292: return false; jtulach@1292: } jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: return true; jtulach@1292: } jtulach@1292: jtulach@1292: private boolean equals(EnumMap em) { jtulach@1292: if (em.keyType != keyType) jtulach@1292: return size == 0 && em.size == 0; jtulach@1292: jtulach@1292: // Key types match, compare each value jtulach@1292: for (int i = 0; i < keyUniverse.length; i++) { jtulach@1292: Object ourValue = vals[i]; jtulach@1292: Object hisValue = em.vals[i]; jtulach@1292: if (hisValue != ourValue && jtulach@1292: (hisValue == null || !hisValue.equals(ourValue))) jtulach@1292: return false; jtulach@1292: } jtulach@1292: return true; jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns the hash code value for this map. The hash code of a map is jtulach@1292: * defined to be the sum of the hash codes of each entry in the map. jtulach@1292: */ jtulach@1292: public int hashCode() { jtulach@1292: int h = 0; jtulach@1292: jtulach@1292: for (int i = 0; i < keyUniverse.length; i++) { jtulach@1292: if (null != vals[i]) { jtulach@1292: h += entryHashCode(i); jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: return h; jtulach@1292: } jtulach@1292: jtulach@1292: private int entryHashCode(int index) { jtulach@1292: return (keyUniverse[index].hashCode() ^ vals[index].hashCode()); jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns a shallow copy of this enum map. (The values themselves jtulach@1292: * are not cloned. jtulach@1292: * jtulach@1292: * @return a shallow copy of this enum map jtulach@1292: */ jtulach@1292: public EnumMap clone() { jtulach@1292: EnumMap result = null; jtulach@1292: try { jtulach@1292: result = (EnumMap) super.clone(); jtulach@1292: } catch(CloneNotSupportedException e) { jtulach@1292: throw new AssertionError(); jtulach@1292: } jtulach@1292: result.vals = result.vals.clone(); jtulach@1292: return result; jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Throws an exception if e is not of the correct type for this enum set. jtulach@1292: */ jtulach@1292: private void typeCheck(K key) { jtulach@1292: Class keyClass = key.getClass(); jtulach@1292: if (keyClass != keyType && keyClass.getSuperclass() != keyType) jtulach@1292: throw new ClassCastException(keyClass + " != " + keyType); jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Returns all of the values comprising K. jtulach@1292: * The result is uncloned, cached, and shared by all callers. jtulach@1292: */ jtulach@1292: private static > K[] getKeyUniverse(Class keyType) { jtulach@1292: return SharedSecrets.getJavaLangAccess() jtulach@1292: .getEnumConstantsShared(keyType); jtulach@1292: } jtulach@1292: jtulach@1292: private static final long serialVersionUID = 458661240069192865L; jtulach@1292: jtulach@1292: /** jtulach@1292: * Save the state of the EnumMap instance to a stream (i.e., jtulach@1292: * serialize it). jtulach@1292: * jtulach@1292: * @serialData The size of the enum map (the number of key-value jtulach@1292: * mappings) is emitted (int), followed by the key (Object) jtulach@1292: * and value (Object) for each key-value mapping represented jtulach@1292: * by the enum map. jtulach@1292: */ jtulach@1292: private void writeObject(java.io.ObjectOutputStream s) jtulach@1292: throws java.io.IOException jtulach@1292: { jtulach@1292: // Write out the key type and any hidden stuff jtulach@1292: s.defaultWriteObject(); jtulach@1292: jtulach@1292: // Write out size (number of Mappings) jtulach@1292: s.writeInt(size); jtulach@1292: jtulach@1292: // Write out keys and values (alternating) jtulach@1292: int entriesToBeWritten = size; jtulach@1292: for (int i = 0; entriesToBeWritten > 0; i++) { jtulach@1292: if (null != vals[i]) { jtulach@1292: s.writeObject(keyUniverse[i]); jtulach@1292: s.writeObject(unmaskNull(vals[i])); jtulach@1292: entriesToBeWritten--; jtulach@1292: } jtulach@1292: } jtulach@1292: } jtulach@1292: jtulach@1292: /** jtulach@1292: * Reconstitute the EnumMap instance from a stream (i.e., jtulach@1292: * deserialize it). jtulach@1292: */ jtulach@1292: private void readObject(java.io.ObjectInputStream s) jtulach@1292: throws java.io.IOException, ClassNotFoundException jtulach@1292: { jtulach@1292: // Read in the key type and any hidden stuff jtulach@1292: s.defaultReadObject(); jtulach@1292: jtulach@1292: keyUniverse = getKeyUniverse(keyType); jtulach@1292: vals = new Object[keyUniverse.length]; jtulach@1292: jtulach@1292: // Read in size (number of Mappings) jtulach@1292: int size = s.readInt(); jtulach@1292: jtulach@1292: // Read the keys and values, and put the mappings in the HashMap jtulach@1292: for (int i = 0; i < size; i++) { jtulach@1292: K key = (K) s.readObject(); jtulach@1292: V value = (V) s.readObject(); jtulach@1292: put(key, value); jtulach@1292: } jtulach@1292: } jtulach@1292: }