EnumMap.java revision 983b2c6ff9ea6d35adf7ab6398dccf870b7e180a
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 26package java.util; 27 28import java.util.Map.Entry; 29 30/** 31 * A specialized {@link Map} implementation for use with enum type keys. All 32 * of the keys in an enum map must come from a single enum type that is 33 * specified, explicitly or implicitly, when the map is created. Enum maps 34 * are represented internally as arrays. This representation is extremely 35 * compact and efficient. 36 * 37 * <p>Enum maps are maintained in the <i>natural order</i> of their keys 38 * (the order in which the enum constants are declared). This is reflected 39 * in the iterators returned by the collections views ({@link #keySet()}, 40 * {@link #entrySet()}, and {@link #values()}). 41 * 42 * <p>Iterators returned by the collection views are <i>weakly consistent</i>: 43 * they will never throw {@link ConcurrentModificationException} and they may 44 * or may not show the effects of any modifications to the map that occur while 45 * the iteration is in progress. 46 * 47 * <p>Null keys are not permitted. Attempts to insert a null key will 48 * throw {@link NullPointerException}. Attempts to test for the 49 * presence of a null key or to remove one will, however, function properly. 50 * Null values are permitted. 51 52 * <P>Like most collection implementations <tt>EnumMap</tt> is not 53 * synchronized. If multiple threads access an enum map concurrently, and at 54 * least one of the threads modifies the map, it should be synchronized 55 * externally. This is typically accomplished by synchronizing on some 56 * object that naturally encapsulates the enum map. If no such object exists, 57 * the map should be "wrapped" using the {@link Collections#synchronizedMap} 58 * method. This is best done at creation time, to prevent accidental 59 * unsynchronized access: 60 * 61 * <pre> 62 * Map<EnumKey, V> m 63 * = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...)); 64 * </pre> 65 * 66 * <p>Implementation note: All basic operations execute in constant time. 67 * They are likely (though not guaranteed) to be faster than their 68 * {@link HashMap} counterparts. 69 * 70 * <p>This class is a member of the 71 * <a href="{@docRoot}/../technotes/guides/collections/index.html"> 72 * Java Collections Framework</a>. 73 * 74 * @author Josh Bloch 75 * @see EnumSet 76 * @since 1.5 77 */ 78public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> 79 implements java.io.Serializable, Cloneable 80{ 81 /** 82 * The <tt>Class</tt> object for the enum type of all the keys of this map. 83 * 84 * @serial 85 */ 86 private final Class<K> keyType; 87 88 /** 89 * All of the values comprising K. (Cached for performance.) 90 */ 91 private transient K[] keyUniverse; 92 93 /** 94 * Array representation of this map. The ith element is the value 95 * to which universe[i] is currently mapped, or null if it isn't 96 * mapped to anything, or NULL if it's mapped to null. 97 */ 98 private transient Object[] vals; 99 100 /** 101 * The number of mappings in this map. 102 */ 103 private transient int size = 0; 104 105 /** 106 * Distinguished non-null value for representing null values. 107 */ 108 private static final Object NULL = new Object() { 109 public int hashCode() { 110 return 0; 111 } 112 113 public String toString() { 114 return "java.util.EnumMap.NULL"; 115 } 116 }; 117 118 private Object maskNull(Object value) { 119 return (value == null ? NULL : value); 120 } 121 122 private V unmaskNull(Object value) { 123 return (V) (value == NULL ? null : value); 124 } 125 126 private static final Enum[] ZERO_LENGTH_ENUM_ARRAY = new Enum[0]; 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 <tt>keyType</tt> is null 133 */ 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 <tt>m</tt> is null 146 */ 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 <tt>EnumMap</tt> 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 <tt>m</tt> is not an 163 * <tt>EnumMap</tt> instance and contains no mappings 164 * @throws NullPointerException if <tt>m</tt> is null 165 */ 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 */ 190 public int size() { 191 return size; 192 } 193 194 /** 195 * Returns <tt>true</tt> 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 <tt>true</tt> if this map maps one or more keys to this value 200 */ 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 <tt>true</tt> 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 <tt>true</tt> if this map contains a mapping for the specified 217 * key 218 */ 219 public boolean containsKey(Object key) { 220 return isValidKey(key) && vals[((Enum)key).ordinal()] != null; 221 } 222 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 */ 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 * <tt>null</tt> if there was no mapping for key. (A <tt>null</tt> 260 * return can also indicate that the map previously associated 261 * <tt>null</tt> with the specified key.) 262 * @throws NullPointerException if the specified key is null 263 */ 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 * <tt>null</tt> if there was no entry for key. (A <tt>null</tt> 281 * return can also indicate that the map previously associated 282 * <tt>null</tt> with the specified key.) 283 */ 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 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 */ 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 */ 331 public void putAll(Map<? extends K, ? extends V> m) { 332 if (m instanceof EnumMap) { 333 EnumMap<? extends K, ? extends V> em = 334 (EnumMap<? extends K, ? extends V>)m; 335 if (em.keyType != keyType) { 336 if (em.isEmpty()) 337 return; 338 throw new ClassCastException(em.keyType + " != " + keyType); 339 } 340 341 for (int i = 0; i < keyUniverse.length; i++) { 342 Object emValue = em.vals[i]; 343 if (emValue != null) { 344 if (vals[i] == null) 345 size++; 346 vals[i] = emValue; 347 } 348 } 349 } else { 350 super.putAll(m); 351 } 352 } 353 354 /** 355 * Removes all mappings from this map. 356 */ 357 public void clear() { 358 Arrays.fill(vals, null); 359 size = 0; 360 } 361 362 // Views 363 364 /** 365 * This field is initialized to contain an instance of the entry set 366 * view the first time this view is requested. The view is stateless, 367 * so there's no reason to create more than one. 368 */ 369 private transient Set<Map.Entry<K,V>> entrySet = null; 370 371 /** 372 * Returns a {@link Set} view of the keys contained in this map. 373 * The returned set obeys the general contract outlined in 374 * {@link Map#keySet()}. The set's iterator will return the keys 375 * in their natural order (the order in which the enum constants 376 * are declared). 377 * 378 * @return a set view of the keys contained in this enum map 379 */ 380 public Set<K> keySet() { 381 Set<K> ks = keySet; 382 if (ks != null) 383 return ks; 384 else 385 return keySet = new KeySet(); 386 } 387 388 private class KeySet extends AbstractSet<K> { 389 public Iterator<K> iterator() { 390 return new KeyIterator(); 391 } 392 public int size() { 393 return size; 394 } 395 public boolean contains(Object o) { 396 return containsKey(o); 397 } 398 public boolean remove(Object o) { 399 int oldSize = size; 400 EnumMap.this.remove(o); 401 return size != oldSize; 402 } 403 public void clear() { 404 EnumMap.this.clear(); 405 } 406 } 407 408 /** 409 * Returns a {@link Collection} view of the values contained in this map. 410 * The returned collection obeys the general contract outlined in 411 * {@link Map#values()}. The collection's iterator will return the 412 * values in the order their corresponding keys appear in map, 413 * which is their natural order (the order in which the enum constants 414 * are declared). 415 * 416 * @return a collection view of the values contained in this map 417 */ 418 public Collection<V> values() { 419 Collection<V> vs = values; 420 if (vs != null) 421 return vs; 422 else 423 return values = new Values(); 424 } 425 426 private class Values extends AbstractCollection<V> { 427 public Iterator<V> iterator() { 428 return new ValueIterator(); 429 } 430 public int size() { 431 return size; 432 } 433 public boolean contains(Object o) { 434 return containsValue(o); 435 } 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 } 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 */ 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>> { 471 public Iterator<Map.Entry<K,V>> iterator() { 472 return new EntryIterator(); 473 } 474 475 public boolean contains(Object o) { 476 if (!(o instanceof Map.Entry)) 477 return false; 478 Map.Entry entry = (Map.Entry)o; 479 return containsMapping(entry.getKey(), entry.getValue()); 480 } 481 public boolean remove(Object o) { 482 if (!(o instanceof Map.Entry)) 483 return false; 484 Map.Entry entry = (Map.Entry)o; 485 return removeMapping(entry.getKey(), entry.getValue()); 486 } 487 public int size() { 488 return size; 489 } 490 public void clear() { 491 EnumMap.this.clear(); 492 } 493 public Object[] toArray() { 494 return fillEntryArray(new Object[size]); 495 } 496 @SuppressWarnings("unchecked") 497 public <T> T[] toArray(T[] a) { 498 int size = size(); 499 if (a.length < size) 500 a = (T[])java.lang.reflect.Array 501 .newInstance(a.getClass().getComponentType(), size); 502 if (a.length > size) 503 a[size] = null; 504 return (T[]) fillEntryArray(a); 505 } 506 private Object[] fillEntryArray(Object[] a) { 507 int j = 0; 508 for (int i = 0; i < vals.length; i++) 509 if (vals[i] != null) 510 a[j++] = new AbstractMap.SimpleEntry<>( 511 keyUniverse[i], unmaskNull(vals[i])); 512 return a; 513 } 514 } 515 516 private abstract class EnumMapIterator<T> implements Iterator<T> { 517 // Lower bound on index of next element to return 518 int index = 0; 519 520 // Index of last returned element, or -1 if none 521 int lastReturnedIndex = -1; 522 523 public boolean hasNext() { 524 while (index < vals.length && vals[index] == null) 525 index++; 526 return index != vals.length; 527 } 528 529 public void remove() { 530 checkLastReturnedIndex(); 531 532 if (vals[lastReturnedIndex] != null) { 533 vals[lastReturnedIndex] = null; 534 size--; 535 } 536 lastReturnedIndex = -1; 537 } 538 539 private void checkLastReturnedIndex() { 540 if (lastReturnedIndex < 0) 541 throw new IllegalStateException(); 542 } 543 } 544 545 private class KeyIterator extends EnumMapIterator<K> { 546 public K next() { 547 if (!hasNext()) 548 throw new NoSuchElementException(); 549 lastReturnedIndex = index++; 550 return keyUniverse[lastReturnedIndex]; 551 } 552 } 553 554 private class ValueIterator extends EnumMapIterator<V> { 555 public V next() { 556 if (!hasNext()) 557 throw new NoSuchElementException(); 558 lastReturnedIndex = index++; 559 return unmaskNull(vals[lastReturnedIndex]); 560 } 561 } 562 563 private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> { 564 private Entry lastReturnedEntry = null; 565 566 public Map.Entry<K,V> next() { 567 if (!hasNext()) 568 throw new NoSuchElementException(); 569 lastReturnedEntry = new Entry(index++); 570 return lastReturnedEntry; 571 } 572 573 public void remove() { 574 lastReturnedIndex = 575 ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index); 576 super.remove(); 577 lastReturnedEntry.index = lastReturnedIndex; 578 lastReturnedEntry = null; 579 } 580 581 private class Entry implements Map.Entry<K,V> { 582 private int index; 583 584 private Entry(int index) { 585 this.index = index; 586 } 587 588 public K getKey() { 589 checkIndexForEntryUse(); 590 return keyUniverse[index]; 591 } 592 593 public V getValue() { 594 checkIndexForEntryUse(); 595 return unmaskNull(vals[index]); 596 } 597 598 public V setValue(V value) { 599 checkIndexForEntryUse(); 600 V oldValue = unmaskNull(vals[index]); 601 vals[index] = maskNull(value); 602 return oldValue; 603 } 604 605 public boolean equals(Object o) { 606 if (index < 0) 607 return o == this; 608 609 if (!(o instanceof Map.Entry)) 610 return false; 611 612 Map.Entry e = (Map.Entry)o; 613 V ourValue = unmaskNull(vals[index]); 614 Object hisValue = e.getValue(); 615 return (e.getKey() == keyUniverse[index] && 616 (ourValue == hisValue || 617 (ourValue != null && ourValue.equals(hisValue)))); 618 } 619 620 public int hashCode() { 621 if (index < 0) 622 return super.hashCode(); 623 624 return entryHashCode(index); 625 } 626 627 public String toString() { 628 if (index < 0) 629 return super.toString(); 630 631 return keyUniverse[index] + "=" 632 + unmaskNull(vals[index]); 633 } 634 635 private void checkIndexForEntryUse() { 636 if (index < 0) 637 throw new IllegalStateException("Entry was removed"); 638 } 639 } 640 } 641 642 // Comparison and hashing 643 644 /** 645 * Compares the specified object with this map for equality. Returns 646 * <tt>true</tt> if the given object is also a map and the two maps 647 * represent the same mappings, as specified in the {@link 648 * Map#equals(Object)} contract. 649 * 650 * @param o the object to be compared for equality with this map 651 * @return <tt>true</tt> if the specified object is equal to this map 652 */ 653 public boolean equals(Object o) { 654 if (this == o) 655 return true; 656 if (o instanceof EnumMap) 657 return equals((EnumMap)o); 658 if (!(o instanceof Map)) 659 return false; 660 661 Map<K,V> m = (Map<K,V>)o; 662 if (size != m.size()) 663 return false; 664 665 for (int i = 0; i < keyUniverse.length; i++) { 666 if (null != vals[i]) { 667 K key = keyUniverse[i]; 668 V value = unmaskNull(vals[i]); 669 if (null == value) { 670 if (!((null == m.get(key)) && m.containsKey(key))) 671 return false; 672 } else { 673 if (!value.equals(m.get(key))) 674 return false; 675 } 676 } 677 } 678 679 return true; 680 } 681 682 private boolean equals(EnumMap em) { 683 if (em.keyType != keyType) 684 return size == 0 && em.size == 0; 685 686 // Key types match, compare each value 687 for (int i = 0; i < keyUniverse.length; i++) { 688 Object ourValue = vals[i]; 689 Object hisValue = em.vals[i]; 690 if (hisValue != ourValue && 691 (hisValue == null || !hisValue.equals(ourValue))) 692 return false; 693 } 694 return true; 695 } 696 697 /** 698 * Returns the hash code value for this map. The hash code of a map is 699 * defined to be the sum of the hash codes of each entry in the map. 700 */ 701 public int hashCode() { 702 int h = 0; 703 704 for (int i = 0; i < keyUniverse.length; i++) { 705 if (null != vals[i]) { 706 h += entryHashCode(i); 707 } 708 } 709 710 return h; 711 } 712 713 private int entryHashCode(int index) { 714 return (keyUniverse[index].hashCode() ^ vals[index].hashCode()); 715 } 716 717 /** 718 * Returns a shallow copy of this enum map. (The values themselves 719 * are not cloned. 720 * 721 * @return a shallow copy of this enum map 722 */ 723 public EnumMap<K, V> clone() { 724 EnumMap<K, V> result = null; 725 try { 726 result = (EnumMap<K, V>) super.clone(); 727 } catch(CloneNotSupportedException e) { 728 throw new AssertionError(); 729 } 730 result.vals = result.vals.clone(); 731 result.entrySet = null; 732 return result; 733 } 734 735 /** 736 * Throws an exception if e is not of the correct type for this enum set. 737 */ 738 private void typeCheck(K key) { 739 Class keyClass = key.getClass(); 740 if (keyClass != keyType && keyClass.getSuperclass() != keyType) 741 throw new ClassCastException(keyClass + " != " + keyType); 742 } 743 744 /** 745 * Returns all of the values comprising K. 746 * The result is uncloned, cached, and shared by all callers. 747 */ 748 private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) { 749 // Android-changed: Use JavaLangAccess directly instead of going through 750 // SharedSecrets. 751 return JavaLangAccess.getEnumConstantsShared(keyType); 752 } 753 754 private static final long serialVersionUID = 458661240069192865L; 755 756 /** 757 * Save the state of the <tt>EnumMap</tt> instance to a stream (i.e., 758 * serialize it). 759 * 760 * @serialData The <i>size</i> of the enum map (the number of key-value 761 * mappings) is emitted (int), followed by the key (Object) 762 * and value (Object) for each key-value mapping represented 763 * by the enum map. 764 */ 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 <tt>EnumMap</tt> instance from a stream (i.e., 787 * deserialize it). 788 */ 789 private void readObject(java.io.ObjectInputStream s) 790 throws java.io.IOException, ClassNotFoundException 791 { 792 // Read in the key type and any hidden stuff 793 s.defaultReadObject(); 794 795 keyUniverse = getKeyUniverse(keyType); 796 vals = new Object[keyUniverse.length]; 797 798 // Read in size (number of Mappings) 799 int size = s.readInt(); 800 801 // Read the keys and values, and put the mappings in the HashMap 802 for (int i = 0; i < size; i++) { 803 K key = (K) s.readObject(); 804 V value = (V) s.readObject(); 805 put(key, value); 806 } 807 } 808} 809