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