参考自:
HashMap和HashTable的异同:
1 HashMap是非线程安全的,HashTable线程安全,
2 有了第一点,那么HashMap肯定效率高一些,HashTable要低一些
3 HashMap的键和值都可以是null,HashTable的键和值都不行。
通过HashTable的
1 public synchronized V put(K key, V value) { 2 // Make sure the value is not null 3 if (value == null) { 4 throw new NullPointerException(); 5 } 6 7 // Makes sure the key is not already in the hashtable. 8 Entry tab[] = table; 9 int hash = key.hashCode();10 int index = (hash & 0x7FFFFFFF) % tab.length;11 @SuppressWarnings("unchecked")12 Entryentry = (Entry )tab[index];13 for(; entry != null ; entry = entry.next) {14 if ((entry.hash == hash) && entry.key.equals(key)) {15 V old = entry.value;16 entry.value = value;17 return old;18 }19 }20 21 addEntry(hash, key, value, index);22 return null;23 }
HashMap.java的put(K key,V value)方法如下:
1 /** 2 * Associates the specified value with the specified key in this map. 3 * If the map previously contained a mapping for the key, the old 4 * value is replaced. 5 * 6 * @param key key with which the specified value is to be associated 7 * @param value value to be associated with the specified key 8 * @return the previous value associated with key, or 9 * null if there was no mapping for key.10 * (A null return can also indicate that the map11 * previously associated null with key.)12 */13 public V put(K key, V value) {14 if (table == EMPTY_TABLE) {15 inflateTable(threshold);16 }17 if (key == null)18 return putForNullKey(value);19 int hash = hash(key);20 int i = indexFor(hash, table.length);21 for (Entrye = table[i]; e != null; e = e.next) {22 Object k;23 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {24 V oldValue = e.value;25 e.value = value;26 e.recordAccess(this);27 return oldValue;28 }29 }30 31 modCount++;32 addEntry(hash, key, value, i);33 return null;34 }
HashTable.java
1 /* 2 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. 3 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 * 21 * 22 * 23 * 24 */ 25 26 package java.util; 27 28 import java.io.*; 29 import java.util.concurrent.ThreadLocalRandom; 30 import java.util.function.BiConsumer; 31 import java.util.function.Function; 32 import java.util.function.BiFunction; 33 34 /** 35 * This class implements a hash table, which maps keys to values. Any 36 * non-null
object can be used as a key or as a value.37 * 38 * To successfully store and retrieve objects from a hashtable, the 39 * objects used as keys must implement the
hashCode
40 * method and theequals
method.41 * 42 * An instance of
Hashtable
has two parameters that affect its 43 * performance: initial capacity and load factor. The 44 * capacity is the number of buckets in the hash table, and the 45 * initial capacity is simply the capacity at the time the hash table 46 * is created. Note that the hash table is open: in the case of a "hash 47 * collision", a single bucket stores multiple entries, which must be searched 48 * sequentially. The load factor 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.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 * Hashtable operations, including get and put).
58 * 59 * The initial capacity controls a tradeoff between wasted space and the 60 * need for
rehash
operations, which are time-consuming. 61 * Norehash
operations will ever occur if the initial 62 * capacity is greater than the maximum number of entries the 63 * Hashtable will contain divided by its load factor. However, 64 * setting the initial capacity too high can waste space.65 * 66 * If many entries are to be made into a
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.70 * 71 * This example creates a hashtable of numbers. It uses the names of 72 * the numbers as keys: 73 *
{ @code 74 * Hashtable79 * 80 *numbers 75 * = new Hashtable (); 76 * numbers.put("one", 1); 77 * numbers.put("two", 2); 78 * numbers.put("three", 3);} To retrieve a number, use the following code: 81 *
{ @code 82 * Integer n = numbers.get("two"); 83 * if (n != null) { 84 * System.out.println("two = " + n); 85 * }}86 * 87 *The iterators returned by the iterator method of the collections 88 * returned by all of this class's "collection view methods" are 89 * fail-fast: 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 * 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 keys and elements methods are 96 * not fail-fast. 97 * 98 *Note that the fail-fast behavior of an iterator cannot be guaranteed 99 * as it is, generally speaking, impossible to make any hard guarantees in the 100 * presence of unsynchronized concurrent modification. Fail-fast iterators 101 * throw ConcurrentModificationException on a best-effort basis. 102 * Therefore, it would be wrong to write a program that depended on this 103 * exception for its correctness: the fail-fast behavior of iterators 104 * should be used only to detect bugs. 105 * 106 *
As of the Java 2 platform v1.2, this class was retrofitted to 107 * implement the {
@link Map} interface, making it a member of the 108 * 109 * 110 * Java Collections Framework. Unlike the new collection 111 * implementations, { @code Hashtable} is synchronized. If a 112 * thread-safe implementation is not needed, it is recommended to use 113 * { @link HashMap} in place of { @code Hashtable}. If a thread-safe 114 * highly-concurrent implementation is desired, then it is recommended 115 * to use { @link java.util.concurrent.ConcurrentHashMap} in place of 116 * { @code Hashtable}. 117 * 118 * @author Arthur van Hoff 119 * @author Josh Bloch 120 * @author Neal Gafter 121 * @see Object#equals(java.lang.Object) 122 * @see Object#hashCode() 123 * @see Hashtable#rehash() 124 * @see Collection 125 * @see Map 126 * @see HashMap 127 * @see TreeMap 128 * @since JDK1.0 129 */ 130 public class Hashtable131 extends Dictionary 132 implements Map , Cloneable, java.io.Serializable { 133 134 /** 135 * The hash table data. 136 */ 137 private transient Entry [] table; 138 139 /** 140 * The total number of entries in the hash table. 141 */ 142 private transient int count; 143 144 /** 145 * The table is rehashed when its size exceeds this threshold. (The 146 * value of this field is (int)(capacity * loadFactor).) 147 * 148 * @serial 149 */ 150 private int threshold; 151 152 /** 153 * The load factor for the hashtable. 154 * 155 * @serial 156 */ 157 private float loadFactor; 158 159 /** 160 * The number of times this Hashtable has been structurally modified 161 * Structural modifications are those that change the number of entries in 162 * the Hashtable or otherwise modify its internal structure (e.g., 163 * rehash). This field is used to make iterators on Collection-views of 164 * the Hashtable fail-fast. (See ConcurrentModificationException). 165 */ 166 private transient int modCount = 0; 167 168 /** use serialVersionUID from JDK 1.0.2 for interoperability */ 169 private static final long serialVersionUID = 1421746759512286392L; 170 171 /** 172 * Constructs a new, empty hashtable with the specified initial 173 * capacity and the specified load factor. 174 * 175 * @param initialCapacity the initial capacity of the hashtable. 176 * @param loadFactor the load factor of the hashtable. 177 * @exception IllegalArgumentException if the initial capacity is less 178 * than zero, or if the load factor is nonpositive. 179 */ 180 public Hashtable(int initialCapacity, float loadFactor) { 181 if (initialCapacity < 0) 182 throw new IllegalArgumentException("Illegal Capacity: "+ 183 initialCapacity); 184 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 185 throw new IllegalArgumentException("Illegal Load: "+loadFactor); 186 187 if (initialCapacity==0) 188 initialCapacity = 1; 189 this.loadFactor = loadFactor; 190 table = new Entry [initialCapacity]; 191 threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); 192 } 193 194 /** 195 * Constructs a new, empty hashtable with the specified initial capacity 196 * and default load factor (0.75). 197 * 198 * @param initialCapacity the initial capacity of the hashtable. 199 * @exception IllegalArgumentException if the initial capacity is less 200 * than zero. 201 */ 202 public Hashtable(int initialCapacity) { 203 this(initialCapacity, 0.75f); 204 } 205 206 /** 207 * Constructs a new, empty hashtable with a default initial capacity (11) 208 * and load factor (0.75). 209 */ 210 public Hashtable() { 211 this(11, 0.75f); 212 } 213 214 /** 215 * Constructs a new hashtable with the same mappings as the given 216 * Map. The hashtable is created with an initial capacity sufficient to 217 * hold the mappings in the given Map and a default load factor (0.75). 218 * 219 * @param t the map whose mappings are to be placed in this map. 220 * @throws NullPointerException if the specified map is null. 221 * @since 1.2 222 */ 223 public Hashtable(Map t) { 224 this(Math.max(2*t.size(), 11), 0.75f); 225 putAll(t); 226 } 227 228 /** 229 * Returns the number of keys in this hashtable. 230 * 231 * @return the number of keys in this hashtable. 232 */ 233 public synchronized int size() { 234 return count; 235 } 236 237 /** 238 * Tests if this hashtable maps no keys to values. 239 * 240 * @return true
if this hashtable maps no keys to values; 241 *false
otherwise. 242 */ 243 public synchronized boolean isEmpty() { 244 return count == 0; 245 } 246 247 /** 248 * Returns an enumeration of the keys in this hashtable. 249 * 250 * @return an enumeration of the keys in this hashtable. 251 * @see Enumeration 252 * @see #elements() 253 * @see #keySet() 254 * @see Map 255 */ 256 public synchronized Enumerationkeys() { 257 return this. getEnumeration(KEYS); 258 } 259 260 /** 261 * Returns an enumeration of the values in this hashtable. 262 * Use the Enumeration methods on the returned object to fetch the elements 263 * sequentially. 264 * 265 * @return an enumeration of the values in this hashtable. 266 * @see java.util.Enumeration 267 * @see #keys() 268 * @see #values() 269 * @see Map 270 */ 271 public synchronized Enumeration elements() { 272 return this. getEnumeration(VALUES); 273 } 274 275 /** 276 * Tests if some key maps into the specified value in this hashtable. 277 * This operation is more expensive than the { @link #containsKey 278 * containsKey} method. 279 * 280 * Note that this method is identical in functionality to 281 * {
@link #containsValue containsValue}, (which is part of the 282 * { @link Map} interface in the collections framework). 283 * 284 * @param value a value to search for 285 * @returntrue
if and only if some key maps to the 286 *value
argument in this hashtable as 287 * determined by the equals method; 288 *false
otherwise. 289 * @exception NullPointerException if the value isnull
290 */ 291 public synchronized boolean contains(Object value) { 292 if (value == null) { 293 throw new NullPointerException(); 294 } 295 296 Entry tab[] = table; 297 for (int i = tab.length ; i-- > 0 ;) { 298 for (Entry e = tab[i] ; e != null ; e = e.next) { 299 if (e.value.equals(value)) { 300 return true; 301 } 302 } 303 } 304 return false; 305 } 306 307 /** 308 * Returns true if this hashtable maps one or more keys to this value. 309 * 310 *Note that this method is identical in functionality to {
@link 311 * #contains contains} (which predates the { @link Map} interface). 312 * 313 * @param value value whose presence in this hashtable is to be tested 314 * @return true if this map maps one or more keys to the 315 * specified value 316 * @throws NullPointerException if the value isnull
317 * @since 1.2 318 */ 319 public boolean containsValue(Object value) { 320 return contains(value); 321 } 322 323 /** 324 * Tests if the specified object is a key in this hashtable. 325 * 326 * @param key possible key 327 * @returntrue
if and only if the specified object 328 * is a key in this hashtable, as determined by the 329 * equals method;false
otherwise. 330 * @throws NullPointerException if the key isnull
331 * @see #contains(Object) 332 */ 333 public synchronized boolean containsKey(Object key) { 334 Entry tab[] = table; 335 int hash = key.hashCode(); 336 int index = (hash & 0x7FFFFFFF) % tab.length; 337 for (Entry e = tab[index] ; e != null ; e = e.next) { 338 if ((e.hash == hash) && e.key.equals(key)) { 339 return true; 340 } 341 } 342 return false; 343 } 344 345 /** 346 * Returns the value to which the specified key is mapped, 347 * or { @code null} if this map contains no mapping for the key. 348 * 349 *More formally, if this map contains a mapping from a key 350 * {
@code k} to a value { @code v} such that { @code (key.equals(k))}, 351 * then this method returns { @code v}; otherwise it returns 352 * { @code null}. (There can be at most one such mapping.) 353 * 354 * @param key the key whose associated value is to be returned 355 * @return the value to which the specified key is mapped, or 356 * { @code null} if this map contains no mapping for the key 357 * @throws NullPointerException if the specified key is null 358 * @see #put(Object, Object) 359 */ 360 @SuppressWarnings("unchecked") 361 public synchronized V get(Object key) { 362 Entry tab[] = table; 363 int hash = key.hashCode(); 364 int index = (hash & 0x7FFFFFFF) % tab.length; 365 for (Entry e = tab[index] ; e != null ; e = e.next) { 366 if ((e.hash == hash) && e.key.equals(key)) { 367 return (V)e.value; 368 } 369 } 370 return null; 371 } 372 373 /** 374 * The maximum size of array to allocate. 375 * Some VMs reserve some header words in an array. 376 * Attempts to allocate larger arrays may result in 377 * OutOfMemoryError: Requested array size exceeds VM limit 378 */ 379 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 380 381 /** 382 * Increases the capacity of and internally reorganizes this 383 * hashtable, in order to accommodate and access its entries more 384 * efficiently. This method is called automatically when the 385 * number of keys in the hashtable exceeds this hashtable's capacity 386 * and load factor. 387 */ 388 @SuppressWarnings("unchecked") 389 protected void rehash() { 390 int oldCapacity = table.length; 391 Entry [] oldMap = table; 392 393 // overflow-conscious code 394 int newCapacity = (oldCapacity << 1) + 1; 395 if (newCapacity - MAX_ARRAY_SIZE > 0) { 396 if (oldCapacity == MAX_ARRAY_SIZE) 397 // Keep running with MAX_ARRAY_SIZE buckets 398 return; 399 newCapacity = MAX_ARRAY_SIZE; 400 } 401 Entry [] newMap = new Entry [newCapacity]; 402 403 modCount++; 404 threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); 405 table = newMap; 406 407 for (int i = oldCapacity ; i-- > 0 ;) { 408 for (Entryold = (Entry )oldMap[i] ; old != null ; ) { 409 Entry e = old; 410 old = old.next; 411 412 int index = (e.hash & 0x7FFFFFFF) % newCapacity; 413 e.next = (Entry )newMap[index]; 414 newMap[index] = e; 415 } 416 } 417 } 418 419 private void addEntry(int hash, K key, V value, int index) { 420 modCount++; 421 422 Entry tab[] = table; 423 if (count >= threshold) { 424 // Rehash the table if the threshold is exceeded 425 rehash(); 426 427 tab = table; 428 hash = key.hashCode(); 429 index = (hash & 0x7FFFFFFF) % tab.length; 430 } 431 432 // Creates the new entry. 433 @SuppressWarnings("unchecked") 434 Entry e = (Entry ) tab[index]; 435 tab[index] = new Entry<>(hash, key, value, e); 436 count++; 437 } 438 439 /** 440 * Maps the specified key
to the specified 441 *value
in this hashtable. Neither the key nor the 442 * value can benull
.443 * 444 * The value can be retrieved by calling the
tab[] = table; 465 // 如果 key 是null,那么在调用hashCode()方法的时候也会抛出空指针异常,20170407,wyl 466 int hash = key.hashCode(); 467 int index = (hash & 0x7FFFFFFF) % tab.length; 468 @SuppressWarnings("unchecked") 469 Entryget
method 445 * with a key that is equal to the original key. 446 * 447 * @param key the hashtable key 448 * @param value the value 449 * @return the previous value of the specified key in this hashtable, 450 * ornull
if it did not have one 451 * @exception NullPointerException if the key or value is 452 *null
453 * @see Object#equals(Object) 454 * @see #get(Object) 455 */ 456 public synchronized V put(K key, V value) { 457 // Make sure the value is not null 458 // 如果 value是null,那么就抛出空指针异常,20170407,wyl 459 if (value == null) { 460 throw new NullPointerException(); 461 } 462 463 // Makes sure the key is not already in the hashtable. 464 Entryentry = (Entry )tab[index]; 470 for(; entry != null ; entry = entry.next) { 471 if ((entry.hash == hash) && entry.key.equals(key)) { 472 V old = entry.value; 473 entry.value = value; 474 return old; 475 } 476 } 477 478 addEntry(hash, key, value, index); 479 return null; 480 } 481 482 /** 483 * Removes the key (and its corresponding value) from this 484 * hashtable. This method does nothing if the key is not in the hashtable. 485 * 486 * @param key the key that needs to be removed 487 * @return the value to which the key had been mapped in this hashtable, 488 * or null
if the key did not have a mapping 489 * @throws NullPointerException if the key isnull
490 */ 491 public synchronized V remove(Object key) { 492 Entry tab[] = table; 493 int hash = key.hashCode(); 494 int index = (hash & 0x7FFFFFFF) % tab.length; 495 @SuppressWarnings("unchecked") 496 Entrye = (Entry )tab[index]; 497 for(Entry prev = null ; e != null ; prev = e, e = e.next) { 498 if ((e.hash == hash) && e.key.equals(key)) { 499 modCount++; 500 if (prev != null) { 501 prev.next = e.next; 502 } else { 503 tab[index] = e.next; 504 } 505 count--; 506 V oldValue = e.value; 507 e.value = null; 508 return oldValue; 509 } 510 } 511 return null; 512 } 513 514 /** 515 * Copies all of the mappings from the specified map to this hashtable. 516 * These mappings will replace any mappings that this hashtable had for any 517 * of the keys currently in the specified map. 518 * 519 * @param t mappings to be stored in this map 520 * @throws NullPointerException if the specified map is null 521 * @since 1.2 522 */ 523 public synchronized void putAll(Map t) { 524 for (Map.Entry e : t.entrySet()) 525 put(e.getKey(), e.getValue()); 526 } 527 528 /** 529 * Clears this hashtable so that it contains no keys. 530 */ 531 public synchronized void clear() { 532 Entry tab[] = table; 533 modCount++; 534 for (int index = tab.length; --index >= 0; ) 535 tab[index] = null; 536 count = 0; 537 } 538 539 /** 540 * Creates a shallow copy of this hashtable. All the structure of the 541 * hashtable itself is copied, but the keys and values are not cloned. 542 * This is a relatively expensive operation. 543 * 544 * @return a clone of the hashtable 545 */ 546 public synchronized Object clone() { 547 try { 548 Hashtable t = (Hashtable )super.clone(); 549 t.table = new Entry [table.length]; 550 for (int i = table.length ; i-- > 0 ; ) { 551 t.table[i] = (table[i] != null) 552 ? (Entry ) table[i].clone() : null; 553 } 554 t.keySet = null; 555 t.entrySet = null; 556 t.values = null; 557 t.modCount = 0; 558 return t; 559 } catch (CloneNotSupportedException e) { 560 // this shouldn't happen, since we are Cloneable 561 throw new InternalError(e); 562 } 563 } 564 565 /** 566 * Returns a string representation of this Hashtable object 567 * in the form of a set of entries, enclosed in braces and separated 568 * by the ASCII characters " , " (comma and space). Each 569 * entry is rendered as the key, an equals sign =, and the 570 * associated element, where the toString method is used to 571 * convert the key and element to strings. 572 * 573 * @return a string representation of this hashtable 574 */ 575 public synchronized String toString() { 576 int max = size() - 1; 577 if (max == -1) 578 return "{}"; 579 580 StringBuilder sb = new StringBuilder(); 581 Iterator > it = entrySet().iterator(); 582 583 sb.append('{'); 584 for (int i = 0; ; i++) { 585 Map.Entry e = it.next(); 586 K key = e.getKey(); 587 V value = e.getValue(); 588 sb.append(key == this ? "(this Map)" : key.toString()); 589 sb.append('='); 590 sb.append(value == this ? "(this Map)" : value.toString()); 591 592 if (i == max) 593 return sb.append('}').toString(); 594 sb.append(", "); 595 } 596 } 597 598 599 private Enumeration getEnumeration(int type) { 600 if (count == 0) { 601 return Collections.emptyEnumeration(); 602 } else { 603 return new Enumerator<>(type, false); 604 } 605 } 606 607 private Iterator getIterator(int type) { 608 if (count == 0) { 609 return Collections.emptyIterator(); 610 } else { 611 return new Enumerator<>(type, true); 612 } 613 } 614 615 // Views 616 617 /** 618 * Each of these fields are initialized to contain an instance of the 619 * appropriate view the first time this view is requested. The views are 620 * stateless, so there's no reason to create more than one of each. 621 */ 622 private transient volatile Set keySet; 623 private transient volatile Set > entrySet; 624 private transient volatile Collection values; 625 626 /** 627 * Returns a { @link Set} view of the keys contained in this map. 628 * The set is backed by the map, so changes to the map are 629 * reflected in the set, and vice-versa. If the map is modified 630 * while an iteration over the set is in progress (except through 631 * the iterator's own remove operation), the results of 632 * the iteration are undefined. The set supports element removal, 633 * which removes the corresponding mapping from the map, via the 634 * Iterator.remove, Set.remove, 635 * removeAll, retainAll, and clear 636 * operations. It does not support the add or addAll 637 * operations. 638 * 639 * @since 1.2 640 */ 641 public Set keySet() { 642 if (keySet == null) 643 keySet = Collections.synchronizedSet(new KeySet(), this); 644 return keySet; 645 } 646 647 private class KeySet extends AbstractSet { 648 public Iterator iterator() { 649 return getIterator(KEYS); 650 } 651 public int size() { 652 return count; 653 } 654 public boolean contains(Object o) { 655 return containsKey(o); 656 } 657 public boolean remove(Object o) { 658 return Hashtable.this.remove(o) != null; 659 } 660 public void clear() { 661 Hashtable.this.clear(); 662 } 663 } 664 665 /** 666 * Returns a { @link Set} view of the mappings contained in this map. 667 * The set is backed by the map, so changes to the map are 668 * reflected in the set, and vice-versa. If the map is modified 669 * while an iteration over the set is in progress (except through 670 * the iterator's own remove operation, or through the 671 * setValue operation on a map entry returned by the 672 * iterator) the results of the iteration are undefined. The set 673 * supports element removal, which removes the corresponding 674 * mapping from the map, via the Iterator.remove, 675 * Set.remove, removeAll, retainAll and 676 * clear operations. It does not support the 677 * add or addAll operations. 678 * 679 * @since 1.2 680 */ 681 public Set > entrySet() { 682 if (entrySet==null) 683 entrySet = Collections.synchronizedSet(new EntrySet(), this); 684 return entrySet; 685 } 686 687 private class EntrySet extends AbstractSet > { 688 public Iterator > iterator() { 689 return getIterator(ENTRIES); 690 } 691 692 public boolean add(Map.Entry o) { 693 return super.add(o); 694 } 695 696 public boolean contains(Object o) { 697 if (!(o instanceof Map.Entry)) 698 return false; 699 Map.Entry entry = (Map.Entry )o; 700 Object key = entry.getKey(); 701 Entry [] tab = table; 702 int hash = key.hashCode(); 703 int index = (hash & 0x7FFFFFFF) % tab.length; 704 705 for (Entry e = tab[index]; e != null; e = e.next) 706 if (e.hash==hash && e.equals(entry)) 707 return true; 708 return false; 709 } 710 711 public boolean remove(Object o) { 712 if (!(o instanceof Map.Entry)) 713 return false; 714 Map.Entry entry = (Map.Entry ) o; 715 Object key = entry.getKey(); 716 Entry [] tab = table; 717 int hash = key.hashCode(); 718 int index = (hash & 0x7FFFFFFF) % tab.length; 719 720 @SuppressWarnings("unchecked") 721 Entry e = (Entry )tab[index]; 722 for(Entry prev = null; e != null; prev = e, e = e.next) { 723 if (e.hash==hash && e.equals(entry)) { 724 modCount++; 725 if (prev != null) 726 prev.next = e.next; 727 else 728 tab[index] = e.next; 729 730 count--; 731 e.value = null; 732 return true; 733 } 734 } 735 return false; 736 } 737 738 public int size() { 739 return count; 740 } 741 742 public void clear() { 743 Hashtable.this.clear(); 744 } 745 } 746 747 /** 748 * Returns a { @link Collection} view of the values contained in this map. 749 * The collection is backed by the map, so changes to the map are 750 * reflected in the collection, and vice-versa. If the map is 751 * modified while an iteration over the collection is in progress 752 * (except through the iterator's own remove operation), 753 * the results of the iteration are undefined. The collection 754 * supports element removal, which removes the corresponding 755 * mapping from the map, via the Iterator.remove, 756 * Collection.remove, removeAll, 757 * retainAll and clear operations. It does not 758 * support the add or addAll operations. 759 * 760 * @since 1.2 761 */ 762 public Collection values() { 763 if (values==null) 764 values = Collections.synchronizedCollection(new ValueCollection(), 765 this); 766 return values; 767 } 768 769 private class ValueCollection extends AbstractCollection { 770 public Iterator iterator() { 771 return getIterator(VALUES); 772 } 773 public int size() { 774 return count; 775 } 776 public boolean contains(Object o) { 777 return containsValue(o); 778 } 779 public void clear() { 780 Hashtable.this.clear(); 781 } 782 } 783 784 // Comparison and hashing 785 786 /** 787 * Compares the specified Object with this Map for equality, 788 * as per the definition in the Map interface. 789 * 790 * @param o object to be compared for equality with this hashtable 791 * @return true if the specified Object is equal to this Map 792 * @see Map#equals(Object) 793 * @since 1.2 794 */ 795 public synchronized boolean equals(Object o) { 796 if (o == this) 797 return true; 798 799 if (!(o instanceof Map)) 800 return false; 801 Map t = (Map ) o; 802 if (t.size() != size()) 803 return false; 804 805 try { 806 Iterator > i = entrySet().iterator(); 807 while (i.hasNext()) { 808 Map.Entry e = i.next(); 809 K key = e.getKey(); 810 V value = e.getValue(); 811 if (value == null) { 812 if (!(t.get(key)==null && t.containsKey(key))) 813 return false; 814 } else { 815 if (!value.equals(t.get(key))) 816 return false; 817 } 818 } 819 } catch (ClassCastException unused) { 820 return false; 821 } catch (NullPointerException unused) { 822 return false; 823 } 824 825 return true; 826 } 827 828 /** 829 * Returns the hash code value for this Map as per the definition in the 830 * Map interface. 831 * 832 * @see Map#hashCode() 833 * @since 1.2 834 */ 835 public synchronized int hashCode() { 836 /* 837 * This code detects the recursion caused by computing the hash code 838 * of a self-referential hash table and prevents the stack overflow 839 * that would otherwise result. This allows certain 1.1-era 840 * applets with self-referential hash tables to work. This code 841 * abuses the loadFactor field to do double-duty as a hashCode 842 * in progress flag, so as not to worsen the space performance. 843 * A negative load factor indicates that hash code computation is 844 * in progress. 845 */ 846 int h = 0; 847 if (count == 0 || loadFactor < 0) 848 return h; // Returns zero 849 850 loadFactor = -loadFactor; // Mark hashCode computation in progress 851 Entry [] tab = table; 852 for (Entry entry : tab) { 853 while (entry != null) { 854 h += entry.hashCode(); 855 entry = entry.next; 856 } 857 } 858 859 loadFactor = -loadFactor; // Mark hashCode computation complete 860 861 return h; 862 } 863 864 @Override 865 public synchronized V getOrDefault(Object key, V defaultValue) { 866 V result = get(key); 867 return (null == result) ? defaultValue : result; 868 } 869 870 @SuppressWarnings("unchecked") 871 @Override 872 public synchronized void forEach(BiConsumer action) { 873 Objects.requireNonNull(action); // explicit check required in case 874 // table is empty. 875 final int expectedModCount = modCount; 876 877 Entry [] tab = table; 878 for (Entry entry : tab) { 879 while (entry != null) { 880 action.accept((K)entry.key, (V)entry.value); 881 entry = entry.next; 882 883 if (expectedModCount != modCount) { 884 throw new ConcurrentModificationException(); 885 } 886 } 887 } 888 } 889 890 @SuppressWarnings("unchecked") 891 @Override 892 public synchronized void replaceAll(BiFunction function) { 893 Objects.requireNonNull(function); // explicit check required in case 894 // table is empty. 895 final int expectedModCount = modCount; 896 897 Entry [] tab = (Entry [])table; 898 for (Entry entry : tab) { 899 while (entry != null) { 900 entry.value = Objects.requireNonNull( 901 function.apply(entry.key, entry.value)); 902 entry = entry.next; 903 904 if (expectedModCount != modCount) { 905 throw new ConcurrentModificationException(); 906 } 907 } 908 } 909 } 910 911 @Override 912 public synchronized V putIfAbsent(K key, V value) { 913 Objects.requireNonNull(value); 914 915 // Makes sure the key is not already in the hashtable. 916 Entry tab[] = table; 917 int hash = key.hashCode(); 918 int index = (hash & 0x7FFFFFFF) % tab.length; 919 @SuppressWarnings("unchecked") 920 Entry entry = (Entry )tab[index]; 921 for (; entry != null; entry = entry.next) { 922 if ((entry.hash == hash) && entry.key.equals(key)) { 923 V old = entry.value; 924 if (old == null) { 925 entry.value = value; 926 } 927 return old; 928 } 929 } 930 931 addEntry(hash, key, value, index); 932 return null; 933 } 934 935 @Override 936 public synchronized boolean remove(Object key, Object value) { 937 Objects.requireNonNull(value); 938 939 Entry tab[] = table; 940 int hash = key.hashCode(); 941 int index = (hash & 0x7FFFFFFF) % tab.length; 942 @SuppressWarnings("unchecked") 943 Entry e = (Entry )tab[index]; 944 for (Entry prev = null; e != null; prev = e, e = e.next) { 945 if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) { 946 modCount++; 947 if (prev != null) { 948 prev.next = e.next; 949 } else { 950 tab[index] = e.next; 951 } 952 count--; 953 e.value = null; 954 return true; 955 } 956 } 957 return false; 958 } 959 960 @Override 961 public synchronized boolean replace(K key, V oldValue, V newValue) { 962 Objects.requireNonNull(oldValue); 963 Objects.requireNonNull(newValue); 964 Entry tab[] = table; 965 int hash = key.hashCode(); 966 int index = (hash & 0x7FFFFFFF) % tab.length; 967 @SuppressWarnings("unchecked") 968 Entry e = (Entry )tab[index]; 969 for (; e != null; e = e.next) { 970 if ((e.hash == hash) && e.key.equals(key)) { 971 if (e.value.equals(oldValue)) { 972 e.value = newValue; 973 return true; 974 } else { 975 return false; 976 } 977 } 978 } 979 return false; 980 } 981 982 @Override 983 public synchronized V replace(K key, V value) { 984 Objects.requireNonNull(value); 985 Entry tab[] = table; 986 int hash = key.hashCode(); 987 int index = (hash & 0x7FFFFFFF) % tab.length; 988 @SuppressWarnings("unchecked") 989 Entry e = (Entry )tab[index]; 990 for (; e != null; e = e.next) { 991 if ((e.hash == hash) && e.key.equals(key)) { 992 V oldValue = e.value; 993 e.value = value; 994 return oldValue; 995 } 996 } 997 return null; 998 } 999 1000 @Override1001 public synchronized V computeIfAbsent(K key, Function mappingFunction) {1002 Objects.requireNonNull(mappingFunction);1003 1004 Entry tab[] = table;1005 int hash = key.hashCode();1006 int index = (hash & 0x7FFFFFFF) % tab.length;1007 @SuppressWarnings("unchecked")1008 Entry e = (Entry )tab[index];1009 for (; e != null; e = e.next) {1010 if (e.hash == hash && e.key.equals(key)) {1011 // Hashtable not accept null value1012 return e.value;1013 }1014 }1015 1016 V newValue = mappingFunction.apply(key);1017 if (newValue != null) {1018 addEntry(hash, key, newValue, index);1019 }1020 1021 return newValue;1022 }1023 1024 @Override1025 public synchronized V computeIfPresent(K key, BiFunction remappingFunction) {1026 Objects.requireNonNull(remappingFunction);1027 1028 Entry tab[] = table;1029 int hash = key.hashCode();1030 int index = (hash & 0x7FFFFFFF) % tab.length;1031 @SuppressWarnings("unchecked")1032 Entry e = (Entry )tab[index];1033 for (Entry prev = null; e != null; prev = e, e = e.next) {1034 if (e.hash == hash && e.key.equals(key)) {1035 V newValue = remappingFunction.apply(key, e.value);1036 if (newValue == null) {1037 modCount++;1038 if (prev != null) {1039 prev.next = e.next;1040 } else {1041 tab[index] = e.next;1042 }1043 count--;1044 } else {1045 e.value = newValue;1046 }1047 return newValue;1048 }1049 }1050 return null;1051 }1052 1053 @Override1054 public synchronized V compute(K key, BiFunction remappingFunction) {1055 Objects.requireNonNull(remappingFunction);1056 1057 Entry tab[] = table;1058 int hash = key.hashCode();1059 int index = (hash & 0x7FFFFFFF) % tab.length;1060 @SuppressWarnings("unchecked")1061 Entry e = (Entry )tab[index];1062 for (Entry prev = null; e != null; prev = e, e = e.next) {1063 if (e.hash == hash && Objects.equals(e.key, key)) {1064 V newValue = remappingFunction.apply(key, e.value);1065 if (newValue == null) {1066 modCount++;1067 if (prev != null) {1068 prev.next = e.next;1069 } else {1070 tab[index] = e.next;1071 }1072 count--;1073 } else {1074 e.value = newValue;1075 }1076 return newValue;1077 }1078 }1079 1080 V newValue = remappingFunction.apply(key, null);1081 if (newValue != null) {1082 addEntry(hash, key, newValue, index);1083 }1084 1085 return newValue;1086 }1087 1088 @Override1089 public synchronized V merge(K key, V value, BiFunction remappingFunction) {1090 Objects.requireNonNull(remappingFunction);1091 1092 Entry tab[] = table;1093 int hash = key.hashCode();1094 int index = (hash & 0x7FFFFFFF) % tab.length;1095 @SuppressWarnings("unchecked")1096 Entry e = (Entry )tab[index];1097 for (Entry prev = null; e != null; prev = e, e = e.next) {1098 if (e.hash == hash && e.key.equals(key)) {1099 V newValue = remappingFunction.apply(e.value, value);1100 if (newValue == null) {1101 modCount++;1102 if (prev != null) {1103 prev.next = e.next;1104 } else {1105 tab[index] = e.next;1106 }1107 count--;1108 } else {1109 e.value = newValue;1110 }1111 return newValue;1112 }1113 }1114 1115 if (value != null) {1116 addEntry(hash, key, value, index);1117 }1118 1119 return value;1120 }1121 1122 /**1123 * Save the state of the Hashtable to a stream (i.e., serialize it).1124 *1125 * @serialData The capacity of the Hashtable (the length of the1126 * bucket array) is emitted (int), followed by the1127 * size of the Hashtable (the number of key-value1128 * mappings), followed by the key (Object) and value (Object)1129 * for each key-value mapping represented by the Hashtable1130 * The key-value mappings are emitted in no particular order.1131 */1132 private void writeObject(java.io.ObjectOutputStream s)1133 throws IOException {1134 Entry This differs from the regular put method in several ways. No1207 * checking for rehashing is necessary since the number of elements1208 * initially in the table is known. The modCount is not incremented1209 * because we are creating a new instance. Also, no return value1210 * is needed.1211 */1212 private void reconstitutionPut(Entry
[] tab, K key, V value)1213 throws StreamCorruptedException1214 {1215 if (value == null) {1216 throw new java.io.StreamCorruptedException();1217 }1218 // Makes sure the key is not already in the hashtable.1219 // This should not happen in deserialized version.1220 int hash = key.hashCode();1221 int index = (hash & 0x7FFFFFFF) % tab.length;1222 for (Entry e = tab[index] ; e != null ; e = e.next) {1223 if ((e.hash == hash) && e.key.equals(key)) {1224 throw new java.io.StreamCorruptedException();1225 }1226 }1227 // Creates the new entry.1228 @SuppressWarnings("unchecked")1229 Entrye = (Entry )tab[index];1230 tab[index] = new Entry<>(hash, key, value, e);1231 count++;1232 }1233 1234 /**1235 * Hashtable bucket collision list entry1236 */1237 private static class Entry implements Map.Entry {1238 final int hash;1239 final K key;1240 V value;1241 Entry next;1242 1243 protected Entry(int hash, K key, V value, Entry next) {1244 this.hash = hash;1245 this.key = key;1246 this.value = value;1247 this.next = next;1248 }1249 1250 @SuppressWarnings("unchecked")1251 protected Object clone() {1252 return new Entry<>(hash, key, value,1253 (next==null ? null : (Entry ) next.clone()));1254 }1255 1256 // Map.Entry Ops1257 1258 public K getKey() {1259 return key;1260 }1261 1262 public V getValue() {1263 return value;1264 }1265 1266 public V setValue(V value) {1267 if (value == null)1268 throw new NullPointerException();1269 1270 V oldValue = this.value;1271 this.value = value;1272 return oldValue;1273 }1274 1275 public boolean equals(Object o) {1276 if (!(o instanceof Map.Entry))1277 return false;1278 Map.Entry e = (Map.Entry )o;1279 1280 return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&1281 (value==null ? e.getValue()==null : value.equals(e.getValue()));1282 }1283 1284 public int hashCode() {1285 return hash ^ Objects.hashCode(value);1286 }1287 1288 public String toString() {1289 return key.toString()+"="+value.toString();1290 }1291 }1292 1293 // Types of Enumerations/Iterations1294 private static final int KEYS = 0;1295 private static final int VALUES = 1;1296 private static final int ENTRIES = 2;1297 1298 /**1299 * A hashtable enumerator class. This class implements both the1300 * Enumeration and Iterator interfaces, but individual instances1301 * can be created with the Iterator methods disabled. This is necessary1302 * to avoid unintentionally increasing the capabilities granted a user1303 * by passing an Enumeration.1304 */1305 private class Enumerator implements Enumeration , Iterator {1306 Entry [] table = Hashtable.this.table;1307 int index = table.length;1308 Entry entry;1309 Entry lastReturned;1310 int type;1311 1312 /**1313 * Indicates whether this Enumerator is serving as an Iterator1314 * or an Enumeration. (true -> Iterator).1315 */1316 boolean iterator;1317 1318 /**1319 * The modCount value that the iterator believes that the backing1320 * Hashtable should have. If this expectation is violated, the iterator1321 * has detected concurrent modification.1322 */1323 protected int expectedModCount = modCount;1324 1325 Enumerator(int type, boolean iterator) {1326 this.type = type;1327 this.iterator = iterator;1328 }1329 1330 public boolean hasMoreElements() {1331 Entry e = entry;1332 int i = index;1333 Entry [] t = table;1334 /* Use locals for faster loop iteration */1335 while (e == null && i > 0) {1336 e = t[--i];1337 }1338 entry = e;1339 index = i;1340 return e != null;1341 }1342 1343 @SuppressWarnings("unchecked")1344 public T nextElement() {1345 Entry et = entry;1346 int i = index;1347 Entry [] t = table;1348 /* Use locals for faster loop iteration */1349 while (et == null && i > 0) {1350 et = t[--i];1351 }1352 entry = et;1353 index = i;1354 if (et != null) {1355 Entry e = lastReturned = entry;1356 entry = e.next;1357 return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);1358 }1359 throw new NoSuchElementException("Hashtable Enumerator");1360 }1361 1362 // Iterator methods1363 public boolean hasNext() {1364 return hasMoreElements();1365 }1366 1367 public T next() {1368 if (modCount != expectedModCount)1369 throw new ConcurrentModificationException();1370 return nextElement();1371 }1372 1373 public void remove() {1374 if (!iterator)1375 throw new UnsupportedOperationException();1376 if (lastReturned == null)1377 throw new IllegalStateException("Hashtable Enumerator");1378 if (modCount != expectedModCount)1379 throw new ConcurrentModificationException();1380 1381 synchronized(Hashtable.this) {1382 Entry [] tab = Hashtable.this.table;1383 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;1384 1385 @SuppressWarnings("unchecked")1386 Entry e = (Entry )tab[index];1387 for(Entry prev = null; e != null; prev = e, e = e.next) {1388 if (e == lastReturned) {1389 modCount++;1390 expectedModCount++;1391 if (prev == null)1392 tab[index] = e.next;1393 else1394 prev.next = e.next;1395 count--;1396 lastReturned = null;1397 return;1398 }1399 }1400 throw new ConcurrentModificationException();1401 }1402 }1403 }1404 }