1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1994, 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 import java.io.*; 30 import java.util.function.BiConsumer; 31 import java.util.function.BiFunction; 32 import java.util.function.Function; 33 import jdk.internal.misc.SharedSecrets; 34 35 /** 36 * This class implements a hash table, which maps keys to values. Any 37 * non-{@code null} object can be used as a key or as a value. <p> 38 * 39 * To successfully store and retrieve objects from a hashtable, the 40 * objects used as keys must implement the {@code hashCode} 41 * method and the {@code equals} method. <p> 42 * 43 * An instance of {@code Hashtable} has two parameters that affect its 44 * performance: <i>initial capacity</i> and <i>load factor</i>. The 45 * <i>capacity</i> is the number of <i>buckets</i> in the hash table, and the 46 * <i>initial capacity</i> is simply the capacity at the time the hash table 47 * is created. Note that the hash table is <i>open</i>: in the case of a "hash 48 * collision", a single bucket stores multiple entries, which must be searched 49 * sequentially. The <i>load factor</i> is a measure of how full the hash 50 * table is allowed to get before its capacity is automatically increased. 51 * The initial capacity and load factor parameters are merely hints to 52 * the implementation. The exact details as to when and whether the rehash 53 * method is invoked are implementation-dependent.<p> 54 * 55 * Generally, the default load factor (.75) offers a good tradeoff between 56 * time and space costs. Higher values decrease the space overhead but 57 * increase the time cost to look up an entry (which is reflected in most 58 * {@code Hashtable} operations, including {@code get} and {@code put}).<p> 59 * 60 * The initial capacity controls a tradeoff between wasted space and the 61 * need for {@code rehash} operations, which are time-consuming. 62 * No {@code rehash} operations will <i>ever</i> occur if the initial 63 * capacity is greater than the maximum number of entries the 64 * {@code Hashtable} will contain divided by its load factor. However, 65 * setting the initial capacity too high can waste space.<p> 66 * 67 * If many entries are to be made into a {@code Hashtable}, 68 * creating it with a sufficiently large capacity may allow the 69 * entries to be inserted more efficiently than letting it perform 70 * automatic rehashing as needed to grow the table. <p> 71 * 72 * This example creates a hashtable of numbers. It uses the names of 73 * the numbers as keys: 74 * <pre> {@code 75 * Hashtable<String, Integer> numbers 76 * = new Hashtable<String, Integer>(); 77 * numbers.put("one", 1); 78 * numbers.put("two", 2); 79 * numbers.put("three", 3);}</pre> 80 * 81 * <p>To retrieve a number, use the following code: 82 * <pre> {@code 83 * Integer n = numbers.get("two"); 84 * if (n != null) { 85 * System.out.println("two = " + n); 86 * }}</pre> 87 * 88 * <p>The iterators returned by the {@code iterator} method of the collections 89 * returned by all of this class's "collection view methods" are 90 * <em>fail-fast</em>: if the Hashtable is structurally modified at any time 91 * after the iterator is created, in any way except through the iterator's own 92 * {@code remove} method, the iterator will throw a {@link 93 * ConcurrentModificationException}. Thus, in the face of concurrent 94 * modification, the iterator fails quickly and cleanly, rather than risking 95 * arbitrary, non-deterministic behavior at an undetermined time in the future. 96 * The Enumerations returned by Hashtable's {@link #keys keys} and 97 * {@link #elements elements} methods are <em>not</em> fail-fast; if the 98 * Hashtable is structurally modified at any time after the enumeration is 99 * created then the results of enumerating are undefined. 100 * 101 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed 102 * as it is, generally speaking, impossible to make any hard guarantees in the 103 * presence of unsynchronized concurrent modification. Fail-fast iterators 104 * throw {@code ConcurrentModificationException} on a best-effort basis. 105 * Therefore, it would be wrong to write a program that depended on this 106 * exception for its correctness: <i>the fail-fast behavior of iterators 107 * should be used only to detect bugs.</i> 108 * 109 * <p>As of the Java 2 platform v1.2, this class was retrofitted to 110 * implement the {@link Map} interface, making it a member of the 111 * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework"> 112 * 113 * Java Collections Framework</a>. Unlike the new collection 114 * implementations, {@code Hashtable} is synchronized. If a 115 * thread-safe implementation is not needed, it is recommended to use 116 * {@link HashMap} in place of {@code Hashtable}. If a thread-safe 117 * highly-concurrent implementation is desired, then it is recommended 118 * to use {@link java.util.concurrent.ConcurrentHashMap} in place of 119 * {@code Hashtable}. 120 * 121 * @param <K> the type of keys maintained by this map 122 * @param <V> the type of mapped values 123 * 124 * @author Arthur van Hoff 125 * @author Josh Bloch 126 * @author Neal Gafter 127 * @see Object#equals(java.lang.Object) 128 * @see Object#hashCode() 129 * @see Hashtable#rehash() 130 * @see Collection 131 * @see Map 132 * @see HashMap 133 * @see TreeMap 134 * @since 1.0 135 */ 136 public class Hashtable<K,V> 137 extends Dictionary<K,V> 138 implements Map<K,V>, Cloneable, java.io.Serializable { 139 140 /** 141 * The hash table data. 142 */ 143 private transient HashtableEntry<?,?>[] table; 144 145 /** 146 * The total number of entries in the hash table. 147 */ 148 private transient int count; 149 150 /** 151 * The table is rehashed when its size exceeds this threshold. (The 152 * value of this field is (int)(capacity * loadFactor).) 153 * 154 * @serial 155 */ 156 private int threshold; 157 158 /** 159 * The load factor for the hashtable. 160 * 161 * @serial 162 */ 163 private float loadFactor; 164 165 /** 166 * The number of times this Hashtable has been structurally modified 167 * Structural modifications are those that change the number of entries in 168 * the Hashtable or otherwise modify its internal structure (e.g., 169 * rehash). This field is used to make iterators on Collection-views of 170 * the Hashtable fail-fast. (See ConcurrentModificationException). 171 */ 172 private transient int modCount = 0; 173 174 /** use serialVersionUID from JDK 1.0.2 for interoperability */ 175 private static final long serialVersionUID = 1421746759512286392L; 176 177 /** 178 * Constructs a new, empty hashtable with the specified initial 179 * capacity and the specified load factor. 180 * 181 * @param initialCapacity the initial capacity of the hashtable. 182 * @param loadFactor the load factor of the hashtable. 183 * @exception IllegalArgumentException if the initial capacity is less 184 * than zero, or if the load factor is nonpositive. 185 */ Hashtable(int initialCapacity, float loadFactor)186 public Hashtable(int initialCapacity, float loadFactor) { 187 if (initialCapacity < 0) 188 throw new IllegalArgumentException("Illegal Capacity: "+ 189 initialCapacity); 190 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 191 throw new IllegalArgumentException("Illegal Load: "+loadFactor); 192 193 if (initialCapacity==0) 194 initialCapacity = 1; 195 this.loadFactor = loadFactor; 196 table = new HashtableEntry<?,?>[initialCapacity]; 197 // Android-changed: Ignore loadFactor when calculating threshold from initialCapacity 198 // threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); 199 threshold = (int)Math.min(initialCapacity, MAX_ARRAY_SIZE + 1); 200 } 201 202 /** 203 * Constructs a new, empty hashtable with the specified initial capacity 204 * and default load factor (0.75). 205 * 206 * @param initialCapacity the initial capacity of the hashtable. 207 * @exception IllegalArgumentException if the initial capacity is less 208 * than zero. 209 */ Hashtable(int initialCapacity)210 public Hashtable(int initialCapacity) { 211 this(initialCapacity, 0.75f); 212 } 213 214 /** 215 * Constructs a new, empty hashtable with a default initial capacity (11) 216 * and load factor (0.75). 217 */ Hashtable()218 public Hashtable() { 219 this(11, 0.75f); 220 } 221 222 /** 223 * Constructs a new hashtable with the same mappings as the given 224 * Map. The hashtable is created with an initial capacity sufficient to 225 * hold the mappings in the given Map and a default load factor (0.75). 226 * 227 * @param t the map whose mappings are to be placed in this map. 228 * @throws NullPointerException if the specified map is null. 229 * @since 1.2 230 */ Hashtable(Map<? extends K, ? extends V> t)231 public Hashtable(Map<? extends K, ? extends V> t) { 232 this(Math.max(2*t.size(), 11), 0.75f); 233 putAll(t); 234 } 235 236 /** 237 * A constructor chained from {@link Properties} keeps Hashtable fields 238 * uninitialized since they are not used. 239 * 240 * @param dummy a dummy parameter 241 */ Hashtable(Void dummy)242 Hashtable(Void dummy) {} 243 244 /** 245 * Returns the number of keys in this hashtable. 246 * 247 * @return the number of keys in this hashtable. 248 */ size()249 public synchronized int size() { 250 return count; 251 } 252 253 /** 254 * Tests if this hashtable maps no keys to values. 255 * 256 * @return {@code true} if this hashtable maps no keys to values; 257 * {@code false} otherwise. 258 */ isEmpty()259 public synchronized boolean isEmpty() { 260 return count == 0; 261 } 262 263 /** 264 * Returns an enumeration of the keys in this hashtable. 265 * Use the Enumeration methods on the returned object to fetch the keys 266 * sequentially. If the hashtable is structurally modified while enumerating 267 * over the keys then the results of enumerating are undefined. 268 * 269 * @return an enumeration of the keys in this hashtable. 270 * @see Enumeration 271 * @see #elements() 272 * @see #keySet() 273 * @see Map 274 */ keys()275 public synchronized Enumeration<K> keys() { 276 return this.<K>getEnumeration(KEYS); 277 } 278 279 /** 280 * Returns an enumeration of the values in this hashtable. 281 * Use the Enumeration methods on the returned object to fetch the elements 282 * sequentially. If the hashtable is structurally modified while enumerating 283 * over the values then the results of enumerating are undefined. 284 * 285 * @return an enumeration of the values in this hashtable. 286 * @see java.util.Enumeration 287 * @see #keys() 288 * @see #values() 289 * @see Map 290 */ elements()291 public synchronized Enumeration<V> elements() { 292 return this.<V>getEnumeration(VALUES); 293 } 294 295 /** 296 * Tests if some key maps into the specified value in this hashtable. 297 * This operation is more expensive than the {@link #containsKey 298 * containsKey} method. 299 * 300 * <p>Note that this method is identical in functionality to 301 * {@link #containsValue containsValue}, (which is part of the 302 * {@link Map} interface in the collections framework). 303 * 304 * @param value a value to search for 305 * @return {@code true} if and only if some key maps to the 306 * {@code value} argument in this hashtable as 307 * determined by the {@code equals} method; 308 * {@code false} otherwise. 309 * @exception NullPointerException if the value is {@code null} 310 */ contains(Object value)311 public synchronized boolean contains(Object value) { 312 if (value == null) { 313 throw new NullPointerException(); 314 } 315 316 HashtableEntry<?,?> tab[] = table; 317 for (int i = tab.length ; i-- > 0 ;) { 318 for (HashtableEntry<?,?> e = tab[i] ; e != null ; e = e.next) { 319 if (e.value.equals(value)) { 320 return true; 321 } 322 } 323 } 324 return false; 325 } 326 327 /** 328 * Returns true if this hashtable maps one or more keys to this value. 329 * 330 * <p>Note that this method is identical in functionality to {@link 331 * #contains contains} (which predates the {@link Map} interface). 332 * 333 * @param value value whose presence in this hashtable is to be tested 334 * @return {@code true} if this map maps one or more keys to the 335 * specified value 336 * @throws NullPointerException if the value is {@code null} 337 * @since 1.2 338 */ containsValue(Object value)339 public boolean containsValue(Object value) { 340 return contains(value); 341 } 342 343 /** 344 * Tests if the specified object is a key in this hashtable. 345 * 346 * @param key possible key 347 * @return {@code true} if and only if the specified object 348 * is a key in this hashtable, as determined by the 349 * {@code equals} method; {@code false} otherwise. 350 * @throws NullPointerException if the key is {@code null} 351 * @see #contains(Object) 352 */ containsKey(Object key)353 public synchronized boolean containsKey(Object key) { 354 HashtableEntry<?,?> tab[] = table; 355 int hash = key.hashCode(); 356 int index = (hash & 0x7FFFFFFF) % tab.length; 357 for (HashtableEntry<?,?> e = tab[index] ; e != null ; e = e.next) { 358 if ((e.hash == hash) && e.key.equals(key)) { 359 return true; 360 } 361 } 362 return false; 363 } 364 365 /** 366 * Returns the value to which the specified key is mapped, 367 * or {@code null} if this map contains no mapping for the key. 368 * 369 * <p>More formally, if this map contains a mapping from a key 370 * {@code k} to a value {@code v} such that {@code (key.equals(k))}, 371 * then this method returns {@code v}; otherwise it returns 372 * {@code null}. (There can be at most one such mapping.) 373 * 374 * @param key the key whose associated value is to be returned 375 * @return the value to which the specified key is mapped, or 376 * {@code null} if this map contains no mapping for the key 377 * @throws NullPointerException if the specified key is null 378 * @see #put(Object, Object) 379 */ 380 @SuppressWarnings("unchecked") get(Object key)381 public synchronized V get(Object key) { 382 HashtableEntry<?,?> tab[] = table; 383 int hash = key.hashCode(); 384 int index = (hash & 0x7FFFFFFF) % tab.length; 385 for (HashtableEntry<?,?> e = tab[index] ; e != null ; e = e.next) { 386 if ((e.hash == hash) && e.key.equals(key)) { 387 return (V)e.value; 388 } 389 } 390 return null; 391 } 392 393 /** 394 * The maximum size of array to allocate. 395 * Some VMs reserve some header words in an array. 396 * Attempts to allocate larger arrays may result in 397 * OutOfMemoryError: Requested array size exceeds VM limit 398 */ 399 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 400 401 /** 402 * Increases the capacity of and internally reorganizes this 403 * hashtable, in order to accommodate and access its entries more 404 * efficiently. This method is called automatically when the 405 * number of keys in the hashtable exceeds this hashtable's capacity 406 * and load factor. 407 */ 408 @SuppressWarnings("unchecked") rehash()409 protected void rehash() { 410 int oldCapacity = table.length; 411 HashtableEntry<?,?>[] oldMap = table; 412 413 // overflow-conscious code 414 int newCapacity = (oldCapacity << 1) + 1; 415 if (newCapacity - MAX_ARRAY_SIZE > 0) { 416 if (oldCapacity == MAX_ARRAY_SIZE) 417 // Keep running with MAX_ARRAY_SIZE buckets 418 return; 419 newCapacity = MAX_ARRAY_SIZE; 420 } 421 HashtableEntry<?,?>[] newMap = new HashtableEntry<?,?>[newCapacity]; 422 423 modCount++; 424 threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); 425 table = newMap; 426 427 for (int i = oldCapacity ; i-- > 0 ;) { 428 for (HashtableEntry<K,V> old = (HashtableEntry<K,V>)oldMap[i] ; old != null ; ) { 429 HashtableEntry<K,V> e = old; 430 old = old.next; 431 432 int index = (e.hash & 0x7FFFFFFF) % newCapacity; 433 e.next = (HashtableEntry<K,V>)newMap[index]; 434 newMap[index] = e; 435 } 436 } 437 } 438 addEntry(int hash, K key, V value, int index)439 private void addEntry(int hash, K key, V value, int index) { 440 HashtableEntry<?,?> tab[] = table; 441 if (count >= threshold) { 442 // Rehash the table if the threshold is exceeded 443 rehash(); 444 445 tab = table; 446 hash = key.hashCode(); 447 index = (hash & 0x7FFFFFFF) % tab.length; 448 } 449 450 // Creates the new entry. 451 @SuppressWarnings("unchecked") 452 HashtableEntry<K,V> e = (HashtableEntry<K,V>) tab[index]; 453 tab[index] = new HashtableEntry<>(hash, key, value, e); 454 count++; 455 modCount++; 456 } 457 458 /** 459 * Maps the specified {@code key} to the specified 460 * {@code value} in this hashtable. Neither the key nor the 461 * value can be {@code null}. <p> 462 * 463 * The value can be retrieved by calling the {@code get} method 464 * with a key that is equal to the original key. 465 * 466 * @param key the hashtable key 467 * @param value the value 468 * @return the previous value of the specified key in this hashtable, 469 * or {@code null} if it did not have one 470 * @exception NullPointerException if the key or value is 471 * {@code null} 472 * @see Object#equals(Object) 473 * @see #get(Object) 474 */ put(K key, V value)475 public synchronized V put(K key, V value) { 476 // Make sure the value is not null 477 if (value == null) { 478 throw new NullPointerException(); 479 } 480 481 // Makes sure the key is not already in the hashtable. 482 HashtableEntry<?,?> tab[] = table; 483 int hash = key.hashCode(); 484 int index = (hash & 0x7FFFFFFF) % tab.length; 485 @SuppressWarnings("unchecked") 486 HashtableEntry<K,V> entry = (HashtableEntry<K,V>)tab[index]; 487 for(; entry != null ; entry = entry.next) { 488 if ((entry.hash == hash) && entry.key.equals(key)) { 489 V old = entry.value; 490 entry.value = value; 491 return old; 492 } 493 } 494 495 addEntry(hash, key, value, index); 496 return null; 497 } 498 499 /** 500 * Removes the key (and its corresponding value) from this 501 * hashtable. This method does nothing if the key is not in the hashtable. 502 * 503 * @param key the key that needs to be removed 504 * @return the value to which the key had been mapped in this hashtable, 505 * or {@code null} if the key did not have a mapping 506 * @throws NullPointerException if the key is {@code null} 507 */ remove(Object key)508 public synchronized V remove(Object key) { 509 HashtableEntry<?,?> tab[] = table; 510 int hash = key.hashCode(); 511 int index = (hash & 0x7FFFFFFF) % tab.length; 512 @SuppressWarnings("unchecked") 513 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 514 for(HashtableEntry<K,V> prev = null ; e != null ; prev = e, e = e.next) { 515 if ((e.hash == hash) && e.key.equals(key)) { 516 if (prev != null) { 517 prev.next = e.next; 518 } else { 519 tab[index] = e.next; 520 } 521 modCount++; 522 count--; 523 V oldValue = e.value; 524 e.value = null; 525 return oldValue; 526 } 527 } 528 return null; 529 } 530 531 /** 532 * Copies all of the mappings from the specified map to this hashtable. 533 * These mappings will replace any mappings that this hashtable had for any 534 * of the keys currently in the specified map. 535 * 536 * @param t mappings to be stored in this map 537 * @throws NullPointerException if the specified map is null 538 * @since 1.2 539 */ putAll(Map<? extends K, ? extends V> t)540 public synchronized void putAll(Map<? extends K, ? extends V> t) { 541 for (Map.Entry<? extends K, ? extends V> e : t.entrySet()) 542 put(e.getKey(), e.getValue()); 543 } 544 545 /** 546 * Clears this hashtable so that it contains no keys. 547 */ clear()548 public synchronized void clear() { 549 HashtableEntry<?,?> tab[] = table; 550 for (int index = tab.length; --index >= 0; ) 551 tab[index] = null; 552 modCount++; 553 count = 0; 554 } 555 556 /** 557 * Creates a shallow copy of this hashtable. All the structure of the 558 * hashtable itself is copied, but the keys and values are not cloned. 559 * This is a relatively expensive operation. 560 * 561 * @return a clone of the hashtable 562 */ clone()563 public synchronized Object clone() { 564 Hashtable<?,?> t = cloneHashtable(); 565 t.table = new HashtableEntry<?,?>[table.length]; 566 for (int i = table.length ; i-- > 0 ; ) { 567 t.table[i] = (table[i] != null) 568 ? (HashtableEntry<?,?>) table[i].clone() : null; 569 } 570 t.keySet = null; 571 t.entrySet = null; 572 t.values = null; 573 t.modCount = 0; 574 return t; 575 } 576 577 /** Calls super.clone() */ cloneHashtable()578 final Hashtable<?,?> cloneHashtable() { 579 try { 580 return (Hashtable<?,?>)super.clone(); 581 } catch (CloneNotSupportedException e) { 582 // this shouldn't happen, since we are Cloneable 583 throw new InternalError(e); 584 } 585 } 586 587 /** 588 * Returns a string representation of this {@code Hashtable} object 589 * in the form of a set of entries, enclosed in braces and separated 590 * by the ASCII characters "<code> , </code>" (comma and space). Each 591 * entry is rendered as the key, an equals sign {@code =}, and the 592 * associated element, where the {@code toString} method is used to 593 * convert the key and element to strings. 594 * 595 * @return a string representation of this hashtable 596 */ toString()597 public synchronized String toString() { 598 int max = size() - 1; 599 if (max == -1) 600 return "{}"; 601 602 StringBuilder sb = new StringBuilder(); 603 Iterator<Map.Entry<K,V>> it = entrySet().iterator(); 604 605 sb.append('{'); 606 for (int i = 0; ; i++) { 607 Map.Entry<K,V> e = it.next(); 608 K key = e.getKey(); 609 V value = e.getValue(); 610 sb.append(key == this ? "(this Map)" : key.toString()); 611 sb.append('='); 612 sb.append(value == this ? "(this Map)" : value.toString()); 613 614 if (i == max) 615 return sb.append('}').toString(); 616 sb.append(", "); 617 } 618 } 619 620 getEnumeration(int type)621 private <T> Enumeration<T> getEnumeration(int type) { 622 if (count == 0) { 623 return Collections.emptyEnumeration(); 624 } else { 625 return new Enumerator<>(type, false); 626 } 627 } 628 getIterator(int type)629 private <T> Iterator<T> getIterator(int type) { 630 if (count == 0) { 631 return Collections.emptyIterator(); 632 } else { 633 return new Enumerator<>(type, true); 634 } 635 } 636 637 // Views 638 639 /** 640 * Each of these fields are initialized to contain an instance of the 641 * appropriate view the first time this view is requested. The views are 642 * stateless, so there's no reason to create more than one of each. 643 */ 644 private transient volatile Set<K> keySet; 645 private transient volatile Set<Map.Entry<K,V>> entrySet; 646 private transient volatile Collection<V> values; 647 648 /** 649 * Returns a {@link Set} view of the keys contained in this map. 650 * The set is backed by the map, so changes to the map are 651 * reflected in the set, and vice-versa. If the map is modified 652 * while an iteration over the set is in progress (except through 653 * the iterator's own {@code remove} operation), the results of 654 * the iteration are undefined. The set supports element removal, 655 * which removes the corresponding mapping from the map, via the 656 * {@code Iterator.remove}, {@code Set.remove}, 657 * {@code removeAll}, {@code retainAll}, and {@code clear} 658 * operations. It does not support the {@code add} or {@code addAll} 659 * operations. 660 * 661 * @since 1.2 662 */ keySet()663 public Set<K> keySet() { 664 if (keySet == null) 665 keySet = Collections.synchronizedSet(new KeySet(), this); 666 return keySet; 667 } 668 669 private class KeySet extends AbstractSet<K> { iterator()670 public Iterator<K> iterator() { 671 return getIterator(KEYS); 672 } size()673 public int size() { 674 return count; 675 } contains(Object o)676 public boolean contains(Object o) { 677 return containsKey(o); 678 } remove(Object o)679 public boolean remove(Object o) { 680 return Hashtable.this.remove(o) != null; 681 } clear()682 public void clear() { 683 Hashtable.this.clear(); 684 } 685 } 686 687 /** 688 * Returns a {@link Set} view of the mappings contained in this map. 689 * The set is backed by the map, so changes to the map are 690 * reflected in the set, and vice-versa. If the map is modified 691 * while an iteration over the set is in progress (except through 692 * the iterator's own {@code remove} operation, or through the 693 * {@code setValue} operation on a map entry returned by the 694 * iterator) the results of the iteration are undefined. The set 695 * supports element removal, which removes the corresponding 696 * mapping from the map, via the {@code Iterator.remove}, 697 * {@code Set.remove}, {@code removeAll}, {@code retainAll} and 698 * {@code clear} operations. It does not support the 699 * {@code add} or {@code addAll} operations. 700 * 701 * @since 1.2 702 */ entrySet()703 public Set<Map.Entry<K,V>> entrySet() { 704 if (entrySet==null) 705 entrySet = Collections.synchronizedSet(new EntrySet(), this); 706 return entrySet; 707 } 708 709 private class EntrySet extends AbstractSet<Map.Entry<K,V>> { iterator()710 public Iterator<Map.Entry<K,V>> iterator() { 711 return getIterator(ENTRIES); 712 } 713 add(Map.Entry<K,V> o)714 public boolean add(Map.Entry<K,V> o) { 715 return super.add(o); 716 } 717 contains(Object o)718 public boolean contains(Object o) { 719 if (!(o instanceof Map.Entry)) 720 return false; 721 Map.Entry<?,?> entry = (Map.Entry<?,?>)o; 722 Object key = entry.getKey(); 723 HashtableEntry<?,?>[] tab = table; 724 int hash = key.hashCode(); 725 int index = (hash & 0x7FFFFFFF) % tab.length; 726 727 for (HashtableEntry<?,?> e = tab[index]; e != null; e = e.next) 728 if (e.hash==hash && e.equals(entry)) 729 return true; 730 return false; 731 } 732 remove(Object o)733 public boolean remove(Object o) { 734 if (!(o instanceof Map.Entry)) 735 return false; 736 Map.Entry<?,?> entry = (Map.Entry<?,?>) o; 737 Object key = entry.getKey(); 738 HashtableEntry<?,?>[] tab = table; 739 int hash = key.hashCode(); 740 int index = (hash & 0x7FFFFFFF) % tab.length; 741 742 @SuppressWarnings("unchecked") 743 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 744 for(HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) { 745 if (e.hash==hash && e.equals(entry)) { 746 if (prev != null) 747 prev.next = e.next; 748 else 749 tab[index] = e.next; 750 751 e.value = null; // clear for gc. 752 modCount++; 753 count--; 754 return true; 755 } 756 } 757 return false; 758 } 759 size()760 public int size() { 761 return count; 762 } 763 clear()764 public void clear() { 765 Hashtable.this.clear(); 766 } 767 } 768 769 /** 770 * Returns a {@link Collection} view of the values contained in this map. 771 * The collection is backed by the map, so changes to the map are 772 * reflected in the collection, and vice-versa. If the map is 773 * modified while an iteration over the collection is in progress 774 * (except through the iterator's own {@code remove} operation), 775 * the results of the iteration are undefined. The collection 776 * supports element removal, which removes the corresponding 777 * mapping from the map, via the {@code Iterator.remove}, 778 * {@code Collection.remove}, {@code removeAll}, 779 * {@code retainAll} and {@code clear} operations. It does not 780 * support the {@code add} or {@code addAll} operations. 781 * 782 * @since 1.2 783 */ values()784 public Collection<V> values() { 785 if (values==null) 786 values = Collections.synchronizedCollection(new ValueCollection(), 787 this); 788 return values; 789 } 790 791 private class ValueCollection extends AbstractCollection<V> { iterator()792 public Iterator<V> iterator() { 793 return getIterator(VALUES); 794 } size()795 public int size() { 796 return count; 797 } contains(Object o)798 public boolean contains(Object o) { 799 return containsValue(o); 800 } clear()801 public void clear() { 802 Hashtable.this.clear(); 803 } 804 } 805 806 // Comparison and hashing 807 808 /** 809 * Compares the specified Object with this Map for equality, 810 * as per the definition in the Map interface. 811 * 812 * @param o object to be compared for equality with this hashtable 813 * @return true if the specified Object is equal to this Map 814 * @see Map#equals(Object) 815 * @since 1.2 816 */ equals(Object o)817 public synchronized boolean equals(Object o) { 818 if (o == this) 819 return true; 820 821 if (!(o instanceof Map)) 822 return false; 823 Map<?,?> t = (Map<?,?>) o; 824 if (t.size() != size()) 825 return false; 826 827 try { 828 for (Map.Entry<K, V> e : entrySet()) { 829 K key = e.getKey(); 830 V value = e.getValue(); 831 if (value == null) { 832 if (!(t.get(key) == null && t.containsKey(key))) 833 return false; 834 } else { 835 if (!value.equals(t.get(key))) 836 return false; 837 } 838 } 839 } catch (ClassCastException unused) { 840 return false; 841 } catch (NullPointerException unused) { 842 return false; 843 } 844 845 return true; 846 } 847 848 /** 849 * Returns the hash code value for this Map as per the definition in the 850 * Map interface. 851 * 852 * @see Map#hashCode() 853 * @since 1.2 854 */ hashCode()855 public synchronized int hashCode() { 856 /* 857 * This code detects the recursion caused by computing the hash code 858 * of a self-referential hash table and prevents the stack overflow 859 * that would otherwise result. This allows certain 1.1-era 860 * applets with self-referential hash tables to work. This code 861 * abuses the loadFactor field to do double-duty as a hashCode 862 * in progress flag, so as not to worsen the space performance. 863 * A negative load factor indicates that hash code computation is 864 * in progress. 865 */ 866 int h = 0; 867 if (count == 0 || loadFactor < 0) 868 return h; // Returns zero 869 870 loadFactor = -loadFactor; // Mark hashCode computation in progress 871 HashtableEntry<?,?>[] tab = table; 872 for (HashtableEntry<?,?> entry : tab) { 873 while (entry != null) { 874 h += entry.hashCode(); 875 entry = entry.next; 876 } 877 } 878 879 loadFactor = -loadFactor; // Mark hashCode computation complete 880 881 return h; 882 } 883 884 @Override getOrDefault(Object key, V defaultValue)885 public synchronized V getOrDefault(Object key, V defaultValue) { 886 V result = get(key); 887 return (null == result) ? defaultValue : result; 888 } 889 890 @SuppressWarnings("unchecked") 891 @Override forEach(BiConsumer<? super K, ? super V> action)892 public synchronized void forEach(BiConsumer<? super K, ? super V> action) { 893 Objects.requireNonNull(action); // explicit check required in case 894 // table is empty. 895 final int expectedModCount = modCount; 896 897 HashtableEntry<?, ?>[] tab = table; 898 for (HashtableEntry<?, ?> entry : tab) { 899 while (entry != null) { 900 action.accept((K)entry.key, (V)entry.value); 901 entry = entry.next; 902 903 if (expectedModCount != modCount) { 904 throw new ConcurrentModificationException(); 905 } 906 } 907 } 908 } 909 910 @SuppressWarnings("unchecked") 911 @Override replaceAll(BiFunction<? super K, ? super V, ? extends V> function)912 public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { 913 Objects.requireNonNull(function); // explicit check required in case 914 // table is empty. 915 final int expectedModCount = modCount; 916 917 HashtableEntry<K, V>[] tab = (HashtableEntry<K, V>[])table; 918 for (HashtableEntry<K, V> entry : tab) { 919 while (entry != null) { 920 entry.value = Objects.requireNonNull( 921 function.apply(entry.key, entry.value)); 922 entry = entry.next; 923 924 if (expectedModCount != modCount) { 925 throw new ConcurrentModificationException(); 926 } 927 } 928 } 929 } 930 931 @Override putIfAbsent(K key, V value)932 public synchronized V putIfAbsent(K key, V value) { 933 Objects.requireNonNull(value); 934 935 // Makes sure the key is not already in the hashtable. 936 HashtableEntry<?,?> tab[] = table; 937 int hash = key.hashCode(); 938 int index = (hash & 0x7FFFFFFF) % tab.length; 939 @SuppressWarnings("unchecked") 940 HashtableEntry<K,V> entry = (HashtableEntry<K,V>)tab[index]; 941 for (; entry != null; entry = entry.next) { 942 if ((entry.hash == hash) && entry.key.equals(key)) { 943 V old = entry.value; 944 if (old == null) { 945 entry.value = value; 946 } 947 return old; 948 } 949 } 950 951 addEntry(hash, key, value, index); 952 return null; 953 } 954 955 @Override remove(Object key, Object value)956 public synchronized boolean remove(Object key, Object value) { 957 Objects.requireNonNull(value); 958 959 HashtableEntry<?,?> tab[] = table; 960 int hash = key.hashCode(); 961 int index = (hash & 0x7FFFFFFF) % tab.length; 962 @SuppressWarnings("unchecked") 963 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 964 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) { 965 if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) { 966 if (prev != null) { 967 prev.next = e.next; 968 } else { 969 tab[index] = e.next; 970 } 971 e.value = null; // clear for gc 972 modCount++; 973 count--; 974 return true; 975 } 976 } 977 return false; 978 } 979 980 @Override replace(K key, V oldValue, V newValue)981 public synchronized boolean replace(K key, V oldValue, V newValue) { 982 Objects.requireNonNull(oldValue); 983 Objects.requireNonNull(newValue); 984 HashtableEntry<?,?> tab[] = table; 985 int hash = key.hashCode(); 986 int index = (hash & 0x7FFFFFFF) % tab.length; 987 @SuppressWarnings("unchecked") 988 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 989 for (; e != null; e = e.next) { 990 if ((e.hash == hash) && e.key.equals(key)) { 991 if (e.value.equals(oldValue)) { 992 e.value = newValue; 993 return true; 994 } else { 995 return false; 996 } 997 } 998 } 999 return false; 1000 } 1001 1002 @Override replace(K key, V value)1003 public synchronized V replace(K key, V value) { 1004 Objects.requireNonNull(value); 1005 HashtableEntry<?,?> tab[] = table; 1006 int hash = key.hashCode(); 1007 int index = (hash & 0x7FFFFFFF) % tab.length; 1008 @SuppressWarnings("unchecked") 1009 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 1010 for (; e != null; e = e.next) { 1011 if ((e.hash == hash) && e.key.equals(key)) { 1012 V oldValue = e.value; 1013 e.value = value; 1014 return oldValue; 1015 } 1016 } 1017 return null; 1018 } 1019 1020 /** 1021 * {@inheritDoc} 1022 * 1023 * <p>This method will, on a best-effort basis, throw a 1024 * {@link java.util.ConcurrentModificationException} if the mapping 1025 * function modified this map during computation. 1026 * 1027 * @throws ConcurrentModificationException if it is detected that the 1028 * mapping function modified this map 1029 */ 1030 @Override computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)1031 public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { 1032 Objects.requireNonNull(mappingFunction); 1033 1034 HashtableEntry<?,?> tab[] = table; 1035 int hash = key.hashCode(); 1036 int index = (hash & 0x7FFFFFFF) % tab.length; 1037 @SuppressWarnings("unchecked") 1038 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 1039 for (; e != null; e = e.next) { 1040 if (e.hash == hash && e.key.equals(key)) { 1041 // Hashtable not accept null value 1042 return e.value; 1043 } 1044 } 1045 1046 int mc = modCount; 1047 V newValue = mappingFunction.apply(key); 1048 if (mc != modCount) { throw new ConcurrentModificationException(); } 1049 if (newValue != null) { 1050 addEntry(hash, key, newValue, index); 1051 } 1052 1053 return newValue; 1054 } 1055 1056 /** 1057 * {@inheritDoc} 1058 * 1059 * <p>This method will, on a best-effort basis, throw a 1060 * {@link java.util.ConcurrentModificationException} if the remapping 1061 * function modified this map during computation. 1062 * 1063 * @throws ConcurrentModificationException if it is detected that the 1064 * remapping function modified this map 1065 */ 1066 @Override computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)1067 public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { 1068 Objects.requireNonNull(remappingFunction); 1069 1070 HashtableEntry<?,?> tab[] = table; 1071 int hash = key.hashCode(); 1072 int index = (hash & 0x7FFFFFFF) % tab.length; 1073 @SuppressWarnings("unchecked") 1074 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 1075 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) { 1076 if (e.hash == hash && e.key.equals(key)) { 1077 int mc = modCount; 1078 V newValue = remappingFunction.apply(key, e.value); 1079 if (mc != modCount) { 1080 throw new ConcurrentModificationException(); 1081 } 1082 if (newValue == null) { 1083 if (prev != null) { 1084 prev.next = e.next; 1085 } else { 1086 tab[index] = e.next; 1087 } 1088 modCount = mc + 1; 1089 count--; 1090 } else { 1091 e.value = newValue; 1092 } 1093 return newValue; 1094 } 1095 } 1096 return null; 1097 } 1098 /** 1099 * {@inheritDoc} 1100 * 1101 * <p>This method will, on a best-effort basis, throw a 1102 * {@link java.util.ConcurrentModificationException} if the remapping 1103 * function modified this map during computation. 1104 * 1105 * @throws ConcurrentModificationException if it is detected that the 1106 * remapping function modified this map 1107 */ 1108 @Override compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)1109 public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { 1110 Objects.requireNonNull(remappingFunction); 1111 1112 HashtableEntry<?,?> tab[] = table; 1113 int hash = key.hashCode(); 1114 int index = (hash & 0x7FFFFFFF) % tab.length; 1115 @SuppressWarnings("unchecked") 1116 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 1117 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) { 1118 if (e.hash == hash && Objects.equals(e.key, key)) { 1119 int mc = modCount; 1120 V newValue = remappingFunction.apply(key, e.value); 1121 if (mc != modCount) { 1122 throw new ConcurrentModificationException(); 1123 } 1124 if (newValue == null) { 1125 if (prev != null) { 1126 prev.next = e.next; 1127 } else { 1128 tab[index] = e.next; 1129 } 1130 modCount = mc + 1; 1131 count--; 1132 } else { 1133 e.value = newValue; 1134 } 1135 return newValue; 1136 } 1137 } 1138 1139 int mc = modCount; 1140 V newValue = remappingFunction.apply(key, null); 1141 if (mc != modCount) { throw new ConcurrentModificationException(); } 1142 if (newValue != null) { 1143 addEntry(hash, key, newValue, index); 1144 } 1145 1146 return newValue; 1147 } 1148 1149 /** 1150 * {@inheritDoc} 1151 * 1152 * <p>This method will, on a best-effort basis, throw a 1153 * {@link java.util.ConcurrentModificationException} if the remapping 1154 * function modified this map during computation. 1155 * 1156 * @throws ConcurrentModificationException if it is detected that the 1157 * remapping function modified this map 1158 */ 1159 @Override merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)1160 public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { 1161 Objects.requireNonNull(remappingFunction); 1162 1163 HashtableEntry<?,?> tab[] = table; 1164 int hash = key.hashCode(); 1165 int index = (hash & 0x7FFFFFFF) % tab.length; 1166 @SuppressWarnings("unchecked") 1167 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 1168 for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) { 1169 if (e.hash == hash && e.key.equals(key)) { 1170 int mc = modCount; 1171 V newValue = remappingFunction.apply(e.value, value); 1172 if (mc != modCount) { 1173 throw new ConcurrentModificationException(); 1174 } 1175 if (newValue == null) { 1176 if (prev != null) { 1177 prev.next = e.next; 1178 } else { 1179 tab[index] = e.next; 1180 } 1181 modCount = mc + 1; 1182 count--; 1183 } else { 1184 e.value = newValue; 1185 } 1186 return newValue; 1187 } 1188 } 1189 1190 if (value != null) { 1191 addEntry(hash, key, value, index); 1192 } 1193 1194 return value; 1195 } 1196 1197 /** 1198 * Save the state of the Hashtable to a stream (i.e., serialize it). 1199 * 1200 * @serialData The <i>capacity</i> of the Hashtable (the length of the 1201 * bucket array) is emitted (int), followed by the 1202 * <i>size</i> of the Hashtable (the number of key-value 1203 * mappings), followed by the key (Object) and value (Object) 1204 * for each key-value mapping represented by the Hashtable 1205 * The key-value mappings are emitted in no particular order. 1206 */ writeObject(java.io.ObjectOutputStream s)1207 private void writeObject(java.io.ObjectOutputStream s) 1208 throws IOException { 1209 writeHashtable(s); 1210 } 1211 1212 /** 1213 * Perform serialization of the Hashtable to an ObjectOutputStream. 1214 * The Properties class overrides this method. 1215 */ writeHashtable(java.io.ObjectOutputStream s)1216 void writeHashtable(java.io.ObjectOutputStream s) 1217 throws IOException { 1218 HashtableEntry<Object, Object> entryStack = null; 1219 1220 synchronized (this) { 1221 // Write out the threshold and loadFactor 1222 s.defaultWriteObject(); 1223 1224 // Write out the length and count of elements 1225 s.writeInt(table.length); 1226 s.writeInt(count); 1227 1228 // Stack copies of the entries in the table 1229 for (HashtableEntry<?, ?> entry : table) { 1230 1231 while (entry != null) { 1232 entryStack = 1233 new HashtableEntry<>(0, entry.key, entry.value, entryStack); 1234 entry = entry.next; 1235 } 1236 } 1237 } 1238 1239 // Write out the key/value objects from the stacked entries 1240 while (entryStack != null) { 1241 s.writeObject(entryStack.key); 1242 s.writeObject(entryStack.value); 1243 entryStack = entryStack.next; 1244 } 1245 } 1246 1247 /** 1248 * Called by Properties to write out a simulated threshold and loadfactor. 1249 */ defaultWriteHashtable(java.io.ObjectOutputStream s, int length, float loadFactor)1250 final void defaultWriteHashtable(java.io.ObjectOutputStream s, int length, 1251 float loadFactor) throws IOException { 1252 // Android-changed: Ignore loadFactor when calculating threshold from initialCapacity 1253 // this.threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); 1254 this.threshold = (int)Math.min(length, MAX_ARRAY_SIZE + 1); 1255 this.loadFactor = loadFactor; 1256 s.defaultWriteObject(); 1257 } 1258 1259 /** 1260 * Reconstitute the Hashtable from a stream (i.e., deserialize it). 1261 */ readObject(java.io.ObjectInputStream s)1262 private void readObject(java.io.ObjectInputStream s) 1263 throws IOException, ClassNotFoundException { 1264 readHashtable(s); 1265 } 1266 1267 /** 1268 * Perform deserialization of the Hashtable from an ObjectInputStream. 1269 * The Properties class overrides this method. 1270 */ readHashtable(java.io.ObjectInputStream s)1271 void readHashtable(java.io.ObjectInputStream s) 1272 throws IOException, ClassNotFoundException { 1273 // Read in the threshold and loadFactor 1274 s.defaultReadObject(); 1275 1276 // Validate loadFactor (ignore threshold - it will be re-computed) 1277 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 1278 throw new StreamCorruptedException("Illegal Load: " + loadFactor); 1279 1280 // Read the original length of the array and number of elements 1281 int origlength = s.readInt(); 1282 int elements = s.readInt(); 1283 1284 // Validate # of elements 1285 if (elements < 0) 1286 throw new StreamCorruptedException("Illegal # of Elements: " + elements); 1287 1288 // Clamp original length to be more than elements / loadFactor 1289 // (this is the invariant enforced with auto-growth) 1290 origlength = Math.max(origlength, (int)(elements / loadFactor) + 1); 1291 1292 // Compute new length with a bit of room 5% + 3 to grow but 1293 // no larger than the clamped original length. Make the length 1294 // odd if it's large enough, this helps distribute the entries. 1295 // Guard against the length ending up zero, that's not valid. 1296 int length = (int)((elements + elements / 20) / loadFactor) + 3; 1297 if (length > elements && (length & 1) == 0) 1298 length--; 1299 length = Math.min(length, origlength); 1300 1301 if (length < 0) { // overflow 1302 length = origlength; 1303 } 1304 1305 // Check Map.Entry[].class since it's the nearest public type to 1306 // what we're actually creating. 1307 SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, length); 1308 table = new HashtableEntry<?,?>[length]; 1309 threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); 1310 count = 0; 1311 1312 // Read the number of elements and then all the key/value objects 1313 for (; elements > 0; elements--) { 1314 @SuppressWarnings("unchecked") 1315 K key = (K)s.readObject(); 1316 @SuppressWarnings("unchecked") 1317 V value = (V)s.readObject(); 1318 // sync is eliminated for performance 1319 reconstitutionPut(table, key, value); 1320 } 1321 } 1322 1323 /** 1324 * The put method used by readObject. This is provided because put 1325 * is overridable and should not be called in readObject since the 1326 * subclass will not yet be initialized. 1327 * 1328 * <p>This differs from the regular put method in several ways. No 1329 * checking for rehashing is necessary since the number of elements 1330 * initially in the table is known. The modCount is not incremented and 1331 * there's no synchronization because we are creating a new instance. 1332 * Also, no return value is needed. 1333 */ reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value)1334 private void reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value) 1335 throws StreamCorruptedException 1336 { 1337 if (value == null) { 1338 throw new java.io.StreamCorruptedException(); 1339 } 1340 // Makes sure the key is not already in the hashtable. 1341 // This should not happen in deserialized version. 1342 int hash = key.hashCode(); 1343 int index = (hash & 0x7FFFFFFF) % tab.length; 1344 for (HashtableEntry<?,?> e = tab[index] ; e != null ; e = e.next) { 1345 if ((e.hash == hash) && e.key.equals(key)) { 1346 throw new java.io.StreamCorruptedException(); 1347 } 1348 } 1349 // Creates the new entry. 1350 @SuppressWarnings("unchecked") 1351 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 1352 tab[index] = new HashtableEntry<>(hash, key, value, e); 1353 count++; 1354 } 1355 1356 /** 1357 * Hashtable bucket collision list entry 1358 */ 1359 // BEGIN Android-changed: Renamed Entry -> HashtableEntry. 1360 // Code references to "HashTable.Entry" must mean Map.Entry 1361 // 1362 // This mirrors the corresponding rename of LinkedHashMap's 1363 // Entry->LinkedHashMapEntry. 1364 // 1365 // This is for source compatibility with earlier versions of Android. 1366 // Otherwise, it would hide Map.Entry which would break compilation 1367 // of code like: 1368 // 1369 // Hashtable.Entry<K, V> entry = hashtable.entrySet().iterator.next(); 1370 // 1371 // To compile, that code snippet's "HashtableMap.Entry" must 1372 // mean java.util.Map.Entry which is the compile time type of 1373 // entrySet()'s elements. 1374 // 1375 private static class HashtableEntry<K,V> implements Map.Entry<K,V> { 1376 // END Android-changed: Renamed Entry -> HashtableEntry. 1377 final int hash; 1378 final K key; 1379 V value; 1380 HashtableEntry<K,V> next; 1381 HashtableEntry(int hash, K key, V value, HashtableEntry<K,V> next)1382 protected HashtableEntry(int hash, K key, V value, HashtableEntry<K,V> next) { 1383 this.hash = hash; 1384 this.key = key; 1385 this.value = value; 1386 this.next = next; 1387 } 1388 1389 @SuppressWarnings("unchecked") clone()1390 protected Object clone() { 1391 return new HashtableEntry<>(hash, key, value, 1392 (next==null ? null : (HashtableEntry<K,V>) next.clone())); 1393 } 1394 1395 // Map.Entry Ops 1396 getKey()1397 public K getKey() { 1398 return key; 1399 } 1400 getValue()1401 public V getValue() { 1402 return value; 1403 } 1404 setValue(V value)1405 public V setValue(V value) { 1406 if (value == null) 1407 throw new NullPointerException(); 1408 1409 V oldValue = this.value; 1410 this.value = value; 1411 return oldValue; 1412 } 1413 equals(Object o)1414 public boolean equals(Object o) { 1415 if (!(o instanceof Map.Entry)) 1416 return false; 1417 Map.Entry<?,?> e = (Map.Entry<?,?>)o; 1418 1419 return (key==null ? e.getKey()==null : key.equals(e.getKey())) && 1420 (value==null ? e.getValue()==null : value.equals(e.getValue())); 1421 } 1422 hashCode()1423 public int hashCode() { 1424 return hash ^ Objects.hashCode(value); 1425 } 1426 toString()1427 public String toString() { 1428 return key.toString()+"="+value.toString(); 1429 } 1430 } 1431 1432 // Types of Enumerations/Iterations 1433 private static final int KEYS = 0; 1434 private static final int VALUES = 1; 1435 private static final int ENTRIES = 2; 1436 1437 /** 1438 * A hashtable enumerator class. This class implements both the 1439 * Enumeration and Iterator interfaces, but individual instances 1440 * can be created with the Iterator methods disabled. This is necessary 1441 * to avoid unintentionally increasing the capabilities granted a user 1442 * by passing an Enumeration. 1443 */ 1444 private class Enumerator<T> implements Enumeration<T>, Iterator<T> { 1445 HashtableEntry<?,?>[] table = Hashtable.this.table; 1446 int index = table.length; 1447 HashtableEntry<?,?> entry; 1448 HashtableEntry<?,?> lastReturned; 1449 final int type; 1450 1451 /** 1452 * Indicates whether this Enumerator is serving as an Iterator 1453 * or an Enumeration. (true -> Iterator). 1454 */ 1455 final boolean iterator; 1456 1457 /** 1458 * The modCount value that the iterator believes that the backing 1459 * Hashtable should have. If this expectation is violated, the iterator 1460 * has detected concurrent modification. 1461 */ 1462 protected int expectedModCount = Hashtable.this.modCount; 1463 Enumerator(int type, boolean iterator)1464 Enumerator(int type, boolean iterator) { 1465 this.type = type; 1466 this.iterator = iterator; 1467 } 1468 hasMoreElements()1469 public boolean hasMoreElements() { 1470 HashtableEntry<?,?> e = entry; 1471 int i = index; 1472 HashtableEntry<?,?>[] t = table; 1473 /* Use locals for faster loop iteration */ 1474 while (e == null && i > 0) { 1475 e = t[--i]; 1476 } 1477 entry = e; 1478 index = i; 1479 return e != null; 1480 } 1481 1482 @SuppressWarnings("unchecked") nextElement()1483 public T nextElement() { 1484 HashtableEntry<?,?> et = entry; 1485 int i = index; 1486 HashtableEntry<?,?>[] t = table; 1487 /* Use locals for faster loop iteration */ 1488 while (et == null && i > 0) { 1489 et = t[--i]; 1490 } 1491 entry = et; 1492 index = i; 1493 if (et != null) { 1494 HashtableEntry<?,?> e = lastReturned = entry; 1495 entry = e.next; 1496 return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e); 1497 } 1498 throw new NoSuchElementException("Hashtable Enumerator"); 1499 } 1500 1501 // Iterator methods hasNext()1502 public boolean hasNext() { 1503 return hasMoreElements(); 1504 } 1505 next()1506 public T next() { 1507 if (Hashtable.this.modCount != expectedModCount) 1508 throw new ConcurrentModificationException(); 1509 return nextElement(); 1510 } 1511 remove()1512 public void remove() { 1513 if (!iterator) 1514 throw new UnsupportedOperationException(); 1515 if (lastReturned == null) 1516 throw new IllegalStateException("Hashtable Enumerator"); 1517 if (modCount != expectedModCount) 1518 throw new ConcurrentModificationException(); 1519 1520 synchronized(Hashtable.this) { 1521 HashtableEntry<?,?>[] tab = Hashtable.this.table; 1522 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; 1523 1524 @SuppressWarnings("unchecked") 1525 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index]; 1526 for(HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) { 1527 if (e == lastReturned) { 1528 if (prev == null) 1529 tab[index] = e.next; 1530 else 1531 prev.next = e.next; 1532 expectedModCount++; 1533 lastReturned = null; 1534 Hashtable.this.modCount++; 1535 Hashtable.this.count--; 1536 return; 1537 } 1538 } 1539 throw new ConcurrentModificationException(); 1540 } 1541 } 1542 } 1543 } 1544