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