History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/util/EnumMap.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/util/EnumMap.java+799−0
@@ -0,0 +1,799 @@
1+/*
2+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package java.util;
27+
28+import java.util.Map.Entry;
29+import sun.misc.SharedSecrets;
30+
31+/**
32+ * A specialized {@link Map} implementation for use with enum type keys. All
33+ * of the keys in an enum map must come from a single enum type that is
34+ * specified, explicitly or implicitly, when the map is created. Enum maps
35+ * are represented internally as arrays. This representation is extremely
36+ * compact and efficient.
37+ *
38+ * <p>Enum maps are maintained in the <i>natural order</i> of their keys
39+ * (the order in which the enum constants are declared). This is reflected
40+ * in the iterators returned by the collections views ({@link #keySet()},
41+ * {@link #entrySet()}, and {@link #values()}).
42+ *
43+ * <p>Iterators returned by the collection views are <i>weakly consistent</i>:
44+ * they will never throw {@link ConcurrentModificationException} and they may
45+ * or may not show the effects of any modifications to the map that occur while
46+ * the iteration is in progress.
47+ *
48+ * <p>Null keys are not permitted. Attempts to insert a null key will
49+ * throw {@link NullPointerException}. Attempts to test for the
50+ * presence of a null key or to remove one will, however, function properly.
51+ * Null values are permitted.
52+
53+ * <P>Like most collection implementations <tt>EnumMap</tt> is not
54+ * synchronized. If multiple threads access an enum map concurrently, and at
55+ * least one of the threads modifies the map, it should be synchronized
56+ * externally. This is typically accomplished by synchronizing on some
57+ * object that naturally encapsulates the enum map. If no such object exists,
58+ * the map should be "wrapped" using the {@link Collections#synchronizedMap}
59+ * method. This is best done at creation time, to prevent accidental
60+ * unsynchronized access:
61+ *
62+ * <pre>
63+ * Map&lt;EnumKey, V&gt; m
64+ * = Collections.synchronizedMap(new EnumMap&lt;EnumKey, V&gt;(...));
65+ * </pre>
66+ *
67+ * <p>Implementation note: All basic operations execute in constant time.
68+ * They are likely (though not guaranteed) to be faster than their
69+ * {@link HashMap} counterparts.
70+ *
71+ * <p>This class is a member of the
72+ * <a href="{@docRoot}/../technotes/guides/collections/index.html">
73+ * Java Collections Framework</a>.
74+ *
75+ * @author Josh Bloch
76+ * @see EnumSet
77+ * @since 1.5
78+ */
79+public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
80+ implements java.io.Serializable, Cloneable
81+{
82+ /**
83+ * The <tt>Class</tt> object for the enum type of all the keys of this map.
84+ *
85+ * @serial
86+ */
87+ private final Class<K> keyType;
88+
89+ /**
90+ * All of the values comprising K. (Cached for performance.)
91+ */
92+ private transient K[] keyUniverse;
93+
94+ /**
95+ * Array representation of this map. The ith element is the value
96+ * to which universe[i] is currently mapped, or null if it isn't
97+ * mapped to anything, or NULL if it's mapped to null.
98+ */
99+ private transient Object[] vals;
100+
101+ /**
102+ * The number of mappings in this map.
103+ */
104+ private transient int size = 0;
105+
106+ /**
107+ * Distinguished non-null value for representing null values.
108+ */
109+ private static final Object NULL = new Integer(0);
110+
111+ private Object maskNull(Object value) {
112+ return (value == null ? NULL : value);
113+ }
114+
115+ private V unmaskNull(Object value) {
116+ return (V) (value == NULL ? null : value);
117+ }
118+
119+ private static final Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0];
120+
121+ /**
122+ * Creates an empty enum map with the specified key type.
123+ *
124+ * @param keyType the class object of the key type for this enum map
125+ * @throws NullPointerException if <tt>keyType</tt> is null
126+ */
127+ public EnumMap(Class<K> keyType) {
128+ this.keyType = keyType;
129+ keyUniverse = getKeyUniverse(keyType);
130+ vals = new Object[keyUniverse.length];
131+ }
132+
133+ /**
134+ * Creates an enum map with the same key type as the specified enum
135+ * map, initially containing the same mappings (if any).
136+ *
137+ * @param m the enum map from which to initialize this enum map
138+ * @throws NullPointerException if <tt>m</tt> is null
139+ */
140+ public EnumMap(EnumMap<K, ? extends V> m) {
141+ keyType = m.keyType;
142+ keyUniverse = m.keyUniverse;
143+ vals = m.vals.clone();
144+ size = m.size;
145+ }
146+
147+ /**
148+ * Creates an enum map initialized from the specified map. If the
149+ * specified map is an <tt>EnumMap</tt> instance, this constructor behaves
150+ * identically to {@link #EnumMap(EnumMap)}. Otherwise, the specified map
151+ * must contain at least one mapping (in order to determine the new
152+ * enum map's key type).
153+ *
154+ * @param m the map from which to initialize this enum map
155+ * @throws IllegalArgumentException if <tt>m</tt> is not an
156+ * <tt>EnumMap</tt> instance and contains no mappings
157+ * @throws NullPointerException if <tt>m</tt> is null
158+ */
159+ public EnumMap(Map<K, ? extends V> m) {
160+ if (m instanceof EnumMap) {
161+ EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
162+ keyType = em.keyType;
163+ keyUniverse = em.keyUniverse;
164+ vals = em.vals.clone();
165+ size = em.size;
166+ } else {
167+ if (m.isEmpty())
168+ throw new IllegalArgumentException("Specified map is empty");
169+ keyType = m.keySet().iterator().next().getDeclaringClass();
170+ keyUniverse = getKeyUniverse(keyType);
171+ vals = new Object[keyUniverse.length];
172+ putAll(m);
173+ }
174+ }
175+
176+ // Query Operations
177+
178+ /**
179+ * Returns the number of key-value mappings in this map.
180+ *
181+ * @return the number of key-value mappings in this map
182+ */
183+ public int size() {
184+ return size;
185+ }
186+
187+ /**
188+ * Returns <tt>true</tt> if this map maps one or more keys to the
189+ * specified value.
190+ *
191+ * @param value the value whose presence in this map is to be tested
192+ * @return <tt>true</tt> if this map maps one or more keys to this value
193+ */
194+ public boolean containsValue(Object value) {
195+ value = maskNull(value);
196+
197+ for (Object val : vals)
198+ if (value.equals(val))
199+ return true;
200+
201+ return false;
202+ }
203+
204+ /**
205+ * Returns <tt>true</tt> if this map contains a mapping for the specified
206+ * key.
207+ *
208+ * @param key the key whose presence in this map is to be tested
209+ * @return <tt>true</tt> if this map contains a mapping for the specified
210+ * key
211+ */
212+ public boolean containsKey(Object key) {
213+ return isValidKey(key) && vals[((Enum)key).ordinal()] != null;
214+ }
215+
216+ private boolean containsMapping(Object key, Object value) {
217+ return isValidKey(key) &&
218+ maskNull(value).equals(vals[((Enum)key).ordinal()]);
219+ }
220+
221+ /**
222+ * Returns the value to which the specified key is mapped,
223+ * or {@code null} if this map contains no mapping for the key.
224+ *
225+ * <p>More formally, if this map contains a mapping from a key
226+ * {@code k} to a value {@code v} such that {@code (key == k)},
227+ * then this method returns {@code v}; otherwise it returns
228+ * {@code null}. (There can be at most one such mapping.)
229+ *
230+ * <p>A return value of {@code null} does not <i>necessarily</i>
231+ * indicate that the map contains no mapping for the key; it's also
232+ * possible that the map explicitly maps the key to {@code null}.
233+ * The {@link #containsKey containsKey} operation may be used to
234+ * distinguish these two cases.
235+ */
236+ public V get(Object key) {
237+ return (isValidKey(key) ?
238+ unmaskNull(vals[((Enum)key).ordinal()]) : null);
239+ }
240+
241+ // Modification Operations
242+
243+ /**
244+ * Associates the specified value with the specified key in this map.
245+ * If the map previously contained a mapping for this key, the old
246+ * value is replaced.
247+ *
248+ * @param key the key with which the specified value is to be associated
249+ * @param value the value to be associated with the specified key
250+ *
251+ * @return the previous value associated with specified key, or
252+ * <tt>null</tt> if there was no mapping for key. (A <tt>null</tt>
253+ * return can also indicate that the map previously associated
254+ * <tt>null</tt> with the specified key.)
255+ * @throws NullPointerException if the specified key is null
256+ */
257+ public V put(K key, V value) {
258+ typeCheck(key);
259+
260+ int index = key.ordinal();
261+ Object oldValue = vals[index];
262+ vals[index] = maskNull(value);
263+ if (oldValue == null)
264+ size++;
265+ return unmaskNull(oldValue);
266+ }
267+
268+ /**
269+ * Removes the mapping for this key from this map if present.
270+ *
271+ * @param key the key whose mapping is to be removed from the map
272+ * @return the previous value associated with specified key, or
273+ * <tt>null</tt> if there was no entry for key. (A <tt>null</tt>
274+ * return can also indicate that the map previously associated
275+ * <tt>null</tt> with the specified key.)
276+ */
277+ public V remove(Object key) {
278+ if (!isValidKey(key))
279+ return null;
280+ int index = ((Enum)key).ordinal();
281+ Object oldValue = vals[index];
282+ vals[index] = null;
283+ if (oldValue != null)
284+ size--;
285+ return unmaskNull(oldValue);
286+ }
287+
288+ private boolean removeMapping(Object key, Object value) {
289+ if (!isValidKey(key))
290+ return false;
291+ int index = ((Enum)key).ordinal();
292+ if (maskNull(value).equals(vals[index])) {
293+ vals[index] = null;
294+ size--;
295+ return true;
296+ }
297+ return false;
298+ }
299+
300+ /**
301+ * Returns true if key is of the proper type to be a key in this
302+ * enum map.
303+ */
304+ private boolean isValidKey(Object key) {
305+ if (key == null)
306+ return false;
307+
308+ // Cheaper than instanceof Enum followed by getDeclaringClass
309+ Class keyClass = key.getClass();
310+ return keyClass == keyType || keyClass.getSuperclass() == keyType;
311+ }
312+
313+ // Bulk Operations
314+
315+ /**
316+ * Copies all of the mappings from the specified map to this map.
317+ * These mappings will replace any mappings that this map had for
318+ * any of the keys currently in the specified map.
319+ *
320+ * @param m the mappings to be stored in this map
321+ * @throws NullPointerException the specified map is null, or if
322+ * one or more keys in the specified map are null
323+ */
324+ public void putAll(Map<? extends K, ? extends V> m) {
325+ if (m instanceof EnumMap) {
326+ EnumMap<? extends K, ? extends V> em =
327+ (EnumMap<? extends K, ? extends V>)m;
328+ if (em.keyType != keyType) {
329+ if (em.isEmpty())
330+ return;
331+ throw new ClassCastException(em.keyType + " != " + keyType);
332+ }
333+
334+ for (int i = 0; i < keyUniverse.length; i++) {
335+ Object emValue = em.vals[i];
336+ if (emValue != null) {
337+ if (vals[i] == null)
338+ size++;
339+ vals[i] = emValue;
340+ }
341+ }
342+ } else {
343+ super.putAll(m);
344+ }
345+ }
346+
347+ /**
348+ * Removes all mappings from this map.
349+ */
350+ public void clear() {
351+ Arrays.fill(vals, null);
352+ size = 0;
353+ }
354+
355+ // Views
356+
357+ /**
358+ * This field is initialized to contain an instance of the entry set
359+ * view the first time this view is requested. The view is stateless,
360+ * so there's no reason to create more than one.
361+ */
362+ private transient Set<Map.Entry<K,V>> entrySet = null;
363+
364+ /**
365+ * Returns a {@link Set} view of the keys contained in this map.
366+ * The returned set obeys the general contract outlined in
367+ * {@link Map#keySet()}. The set's iterator will return the keys
368+ * in their natural order (the order in which the enum constants
369+ * are declared).
370+ *
371+ * @return a set view of the keys contained in this enum map
372+ */
373+ public Set<K> keySet() {
374+ Set<K> ks = keySet;
375+ if (ks != null)
376+ return ks;
377+ else
378+ return keySet = new KeySet();
379+ }
380+
381+ private class KeySet extends AbstractSet<K> {
382+ public Iterator<K> iterator() {
383+ return new KeyIterator();
384+ }
385+ public int size() {
386+ return size;
387+ }
388+ public boolean contains(Object o) {
389+ return containsKey(o);
390+ }
391+ public boolean remove(Object o) {
392+ int oldSize = size;
393+ EnumMap.this.remove(o);
394+ return size != oldSize;
395+ }
396+ public void clear() {
397+ EnumMap.this.clear();
398+ }
399+ }
400+
401+ /**
402+ * Returns a {@link Collection} view of the values contained in this map.
403+ * The returned collection obeys the general contract outlined in
404+ * {@link Map#values()}. The collection's iterator will return the
405+ * values in the order their corresponding keys appear in map,
406+ * which is their natural order (the order in which the enum constants
407+ * are declared).
408+ *
409+ * @return a collection view of the values contained in this map
410+ */
411+ public Collection<V> values() {
412+ Collection<V> vs = values;
413+ if (vs != null)
414+ return vs;
415+ else
416+ return values = new Values();
417+ }
418+
419+ private class Values extends AbstractCollection<V> {
420+ public Iterator<V> iterator() {
421+ return new ValueIterator();
422+ }
423+ public int size() {
424+ return size;
425+ }
426+ public boolean contains(Object o) {
427+ return containsValue(o);
428+ }
429+ public boolean remove(Object o) {
430+ o = maskNull(o);
431+
432+ for (int i = 0; i < vals.length; i++) {
433+ if (o.equals(vals[i])) {
434+ vals[i] = null;
435+ size--;
436+ return true;
437+ }
438+ }
439+ return false;
440+ }
441+ public void clear() {
442+ EnumMap.this.clear();
443+ }
444+ }
445+
446+ /**
447+ * Returns a {@link Set} view of the mappings contained in this map.
448+ * The returned set obeys the general contract outlined in
449+ * {@link Map#keySet()}. The set's iterator will return the
450+ * mappings in the order their keys appear in map, which is their
451+ * natural order (the order in which the enum constants are declared).
452+ *
453+ * @return a set view of the mappings contained in this enum map
454+ */
455+ public Set<Map.Entry<K,V>> entrySet() {
456+ Set<Map.Entry<K,V>> es = entrySet;
457+ if (es != null)
458+ return es;
459+ else
460+ return entrySet = new EntrySet();
461+ }
462+
463+ private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
464+ public Iterator<Map.Entry<K,V>> iterator() {
465+ return new EntryIterator();
466+ }
467+
468+ public boolean contains(Object o) {
469+ if (!(o instanceof Map.Entry))
470+ return false;
471+ Map.Entry entry = (Map.Entry)o;
472+ return containsMapping(entry.getKey(), entry.getValue());
473+ }
474+ public boolean remove(Object o) {
475+ if (!(o instanceof Map.Entry))
476+ return false;
477+ Map.Entry entry = (Map.Entry)o;
478+ return removeMapping(entry.getKey(), entry.getValue());
479+ }
480+ public int size() {
481+ return size;
482+ }
483+ public void clear() {
484+ EnumMap.this.clear();
485+ }
486+ public Object[] toArray() {
487+ return fillEntryArray(new Object[size]);
488+ }
489+ @SuppressWarnings("unchecked")
490+ public <T> T[] toArray(T[] a) {
491+ int size = size();
492+ if (a.length < size)
493+ a = (T[])java.lang.reflect.Array
494+ .newInstance(a.getClass().getComponentType(), size);
495+ if (a.length > size)
496+ a[size] = null;
497+ return (T[]) fillEntryArray(a);
498+ }
499+ private Object[] fillEntryArray(Object[] a) {
500+ int j = 0;
501+ for (int i = 0; i < vals.length; i++)
502+ if (vals[i] != null)
503+ a[j++] = new AbstractMap.SimpleEntry<>(
504+ keyUniverse[i], unmaskNull(vals[i]));
505+ return a;
506+ }
507+ }
508+
509+ private abstract class EnumMapIterator<T> implements Iterator<T> {
510+ // Lower bound on index of next element to return
511+ int index = 0;
512+
513+ // Index of last returned element, or -1 if none
514+ int lastReturnedIndex = -1;
515+
516+ public boolean hasNext() {
517+ while (index < vals.length && vals[index] == null)
518+ index++;
519+ return index != vals.length;
520+ }
521+
522+ public void remove() {
523+ checkLastReturnedIndex();
524+
525+ if (vals[lastReturnedIndex] != null) {
526+ vals[lastReturnedIndex] = null;
527+ size--;
528+ }
529+ lastReturnedIndex = -1;
530+ }
531+
532+ private void checkLastReturnedIndex() {
533+ if (lastReturnedIndex < 0)
534+ throw new IllegalStateException();
535+ }
536+ }
537+
538+ private class KeyIterator extends EnumMapIterator<K> {
539+ public K next() {
540+ if (!hasNext())
541+ throw new NoSuchElementException();
542+ lastReturnedIndex = index++;
543+ return keyUniverse[lastReturnedIndex];
544+ }
545+ }
546+
547+ private class ValueIterator extends EnumMapIterator<V> {
548+ public V next() {
549+ if (!hasNext())
550+ throw new NoSuchElementException();
551+ lastReturnedIndex = index++;
552+ return unmaskNull(vals[lastReturnedIndex]);
553+ }
554+ }
555+
556+ private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> {
557+ private Entry lastReturnedEntry = null;
558+
559+ public Map.Entry<K,V> next() {
560+ if (!hasNext())
561+ throw new NoSuchElementException();
562+ lastReturnedEntry = new Entry(index++);
563+ return lastReturnedEntry;
564+ }
565+
566+ public void remove() {
567+ lastReturnedIndex =
568+ ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
569+ super.remove();
570+ lastReturnedEntry.index = lastReturnedIndex;
571+ lastReturnedEntry = null;
572+ }
573+
574+ private class Entry implements Map.Entry<K,V> {
575+ private int index;
576+
577+ private Entry(int index) {
578+ this.index = index;
579+ }
580+
581+ public K getKey() {
582+ checkIndexForEntryUse();
583+ return keyUniverse[index];
584+ }
585+
586+ public V getValue() {
587+ checkIndexForEntryUse();
588+ return unmaskNull(vals[index]);
589+ }
590+
591+ public V setValue(V value) {
592+ checkIndexForEntryUse();
593+ V oldValue = unmaskNull(vals[index]);
594+ vals[index] = maskNull(value);
595+ return oldValue;
596+ }
597+
598+ public boolean equals(Object o) {
599+ if (index < 0)
600+ return o == this;
601+
602+ if (!(o instanceof Map.Entry))
603+ return false;
604+
605+ Map.Entry e = (Map.Entry)o;
606+ V ourValue = unmaskNull(vals[index]);
607+ Object hisValue = e.getValue();
608+ return (e.getKey() == keyUniverse[index] &&
609+ (ourValue == hisValue ||
610+ (ourValue != null && ourValue.equals(hisValue))));
611+ }
612+
613+ public int hashCode() {
614+ if (index < 0)
615+ return super.hashCode();
616+
617+ return entryHashCode(index);
618+ }
619+
620+ public String toString() {
621+ if (index < 0)
622+ return super.toString();
623+
624+ return keyUniverse[index] + "="
625+ + unmaskNull(vals[index]);
626+ }
627+
628+ private void checkIndexForEntryUse() {
629+ if (index < 0)
630+ throw new IllegalStateException("Entry was removed");
631+ }
632+ }
633+ }
634+
635+ // Comparison and hashing
636+
637+ /**
638+ * Compares the specified object with this map for equality. Returns
639+ * <tt>true</tt> if the given object is also a map and the two maps
640+ * represent the same mappings, as specified in the {@link
641+ * Map#equals(Object)} contract.
642+ *
643+ * @param o the object to be compared for equality with this map
644+ * @return <tt>true</tt> if the specified object is equal to this map
645+ */
646+ public boolean equals(Object o) {
647+ if (this == o)
648+ return true;
649+ if (o instanceof EnumMap)
650+ return equals((EnumMap)o);
651+ if (!(o instanceof Map))
652+ return false;
653+
654+ Map<K,V> m = (Map<K,V>)o;
655+ if (size != m.size())
656+ return false;
657+
658+ for (int i = 0; i < keyUniverse.length; i++) {
659+ if (null != vals[i]) {
660+ K key = keyUniverse[i];
661+ V value = unmaskNull(vals[i]);
662+ if (null == value) {
663+ if (!((null == m.get(key)) && m.containsKey(key)))
664+ return false;
665+ } else {
666+ if (!value.equals(m.get(key)))
667+ return false;
668+ }
669+ }
670+ }
671+
672+ return true;
673+ }
674+
675+ private boolean equals(EnumMap em) {
676+ if (em.keyType != keyType)
677+ return size == 0 && em.size == 0;
678+
679+ // Key types match, compare each value
680+ for (int i = 0; i < keyUniverse.length; i++) {
681+ Object ourValue = vals[i];
682+ Object hisValue = em.vals[i];
683+ if (hisValue != ourValue &&
684+ (hisValue == null || !hisValue.equals(ourValue)))
685+ return false;
686+ }
687+ return true;
688+ }
689+
690+ /**
691+ * Returns the hash code value for this map. The hash code of a map is
692+ * defined to be the sum of the hash codes of each entry in the map.
693+ */
694+ public int hashCode() {
695+ int h = 0;
696+
697+ for (int i = 0; i < keyUniverse.length; i++) {
698+ if (null != vals[i]) {
699+ h += entryHashCode(i);
700+ }
701+ }
702+
703+ return h;
704+ }
705+
706+ private int entryHashCode(int index) {
707+ return (keyUniverse[index].hashCode() ^ vals[index].hashCode());
708+ }
709+
710+ /**
711+ * Returns a shallow copy of this enum map. (The values themselves
712+ * are not cloned.
713+ *
714+ * @return a shallow copy of this enum map
715+ */
716+ public EnumMap<K, V> clone() {
717+ EnumMap<K, V> result = null;
718+ try {
719+ result = (EnumMap<K, V>) super.clone();
720+ } catch(CloneNotSupportedException e) {
721+ throw new AssertionError();
722+ }
723+ result.vals = result.vals.clone();
724+ return result;
725+ }
726+
727+ /**
728+ * Throws an exception if e is not of the correct type for this enum set.
729+ */
730+ private void typeCheck(K key) {
731+ Class keyClass = key.getClass();
732+ if (keyClass != keyType && keyClass.getSuperclass() != keyType)
733+ throw new ClassCastException(keyClass + " != " + keyType);
734+ }
735+
736+ /**
737+ * Returns all of the values comprising K.
738+ * The result is uncloned, cached, and shared by all callers.
739+ */
740+ private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
741+ return SharedSecrets.getJavaLangAccess()
742+ .getEnumConstantsShared(keyType);
743+ }
744+
745+ private static final long serialVersionUID = 458661240069192865L;
746+
747+ /**
748+ * Save the state of the <tt>EnumMap</tt> instance to a stream (i.e.,
749+ * serialize it).
750+ *
751+ * @serialData The <i>size</i> of the enum map (the number of key-value
752+ * mappings) is emitted (int), followed by the key (Object)
753+ * and value (Object) for each key-value mapping represented
754+ * by the enum map.
755+ */
756+ private void writeObject(java.io.ObjectOutputStream s)
757+ throws java.io.IOException
758+ {
759+ // Write out the key type and any hidden stuff
760+ s.defaultWriteObject();
761+
762+ // Write out size (number of Mappings)
763+ s.writeInt(size);
764+
765+ // Write out keys and values (alternating)
766+ int entriesToBeWritten = size;
767+ for (int i = 0; entriesToBeWritten > 0; i++) {
768+ if (null != vals[i]) {
769+ s.writeObject(keyUniverse[i]);
770+ s.writeObject(unmaskNull(vals[i]));
771+ entriesToBeWritten--;
772+ }
773+ }
774+ }
775+
776+ /**
777+ * Reconstitute the <tt>EnumMap</tt> instance from a stream (i.e.,
778+ * deserialize it).
779+ */
780+ private void readObject(java.io.ObjectInputStream s)
781+ throws java.io.IOException, ClassNotFoundException
782+ {
783+ // Read in the key type and any hidden stuff
784+ s.defaultReadObject();
785+
786+ keyUniverse = getKeyUniverse(keyType);
787+ vals = new Object[keyUniverse.length];
788+
789+ // Read in size (number of Mappings)
790+ int size = s.readInt();
791+
792+ // Read the keys and values, and put the mappings in the HashMap
793+ for (int i = 0; i < size; i++) {
794+ K key = (K) s.readObject();
795+ V value = (V) s.readObject();
796+ put(key, value);
797+ }
798+ }
799+}