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