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