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