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 extends K, ? extends V> m) {
jtulach@1292: if (m instanceof EnumMap) {
jtulach@1292: EnumMap extends K, ? extends V> em =
jtulach@1292: (EnumMap extends K, ? extends V>)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: }