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