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