• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1994, 2023, 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 | NullPointerException unused) {
836             return false;
837         }
838 
839         return true;
840     }
841 
842     /**
843      * Returns the hash code value for this Map as per the definition in the
844      * Map interface.
845      *
846      * @see Map#hashCode()
847      * @since 1.2
848      */
hashCode()849     public synchronized int hashCode() {
850         /*
851          * This code detects the recursion caused by computing the hash code
852          * of a self-referential hash table and prevents the stack overflow
853          * that would otherwise result.  This allows certain 1.1-era
854          * applets with self-referential hash tables to work.  This code
855          * abuses the loadFactor field to do double-duty as a hashCode
856          * in progress flag, so as not to worsen the space performance.
857          * A negative load factor indicates that hash code computation is
858          * in progress.
859          */
860         int h = 0;
861         if (count == 0 || loadFactor < 0)
862             return h;  // Returns zero
863 
864         loadFactor = -loadFactor;  // Mark hashCode computation in progress
865         HashtableEntry<?,?>[] tab = table;
866         for (HashtableEntry<?,?> entry : tab) {
867             while (entry != null) {
868                 h += entry.hashCode();
869                 entry = entry.next;
870             }
871         }
872 
873         loadFactor = -loadFactor;  // Mark hashCode computation complete
874 
875         return h;
876     }
877 
878     @Override
getOrDefault(Object key, V defaultValue)879     public synchronized V getOrDefault(Object key, V defaultValue) {
880         V result = get(key);
881         return (null == result) ? defaultValue : result;
882     }
883 
884     @SuppressWarnings("unchecked")
885     @Override
forEach(BiConsumer<? super K, ? super V> action)886     public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
887         Objects.requireNonNull(action);     // explicit check required in case
888                                             // table is empty.
889         final int expectedModCount = modCount;
890 
891         HashtableEntry<?, ?>[] tab = table;
892         for (HashtableEntry<?, ?> entry : tab) {
893             while (entry != null) {
894                 action.accept((K)entry.key, (V)entry.value);
895                 entry = entry.next;
896 
897                 if (expectedModCount != modCount) {
898                     throw new ConcurrentModificationException();
899                 }
900             }
901         }
902     }
903 
904     @SuppressWarnings("unchecked")
905     @Override
replaceAll(BiFunction<? super K, ? super V, ? extends V> function)906     public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
907         Objects.requireNonNull(function);     // explicit check required in case
908                                               // table is empty.
909         final int expectedModCount = modCount;
910 
911         HashtableEntry<K, V>[] tab = (HashtableEntry<K, V>[])table;
912         for (HashtableEntry<K, V> entry : tab) {
913             while (entry != null) {
914                 entry.value = Objects.requireNonNull(
915                     function.apply(entry.key, entry.value));
916                 entry = entry.next;
917 
918                 if (expectedModCount != modCount) {
919                     throw new ConcurrentModificationException();
920                 }
921             }
922         }
923     }
924 
925     @Override
putIfAbsent(K key, V value)926     public synchronized V putIfAbsent(K key, V value) {
927         Objects.requireNonNull(value);
928 
929         // Makes sure the key is not already in the hashtable.
930         HashtableEntry<?,?> tab[] = table;
931         int hash = key.hashCode();
932         int index = (hash & 0x7FFFFFFF) % tab.length;
933         @SuppressWarnings("unchecked")
934         HashtableEntry<K,V> entry = (HashtableEntry<K,V>)tab[index];
935         for (; entry != null; entry = entry.next) {
936             if ((entry.hash == hash) && entry.key.equals(key)) {
937                 V old = entry.value;
938                 if (old == null) {
939                     entry.value = value;
940                 }
941                 return old;
942             }
943         }
944 
945         addEntry(hash, key, value, index);
946         return null;
947     }
948 
949     @Override
remove(Object key, Object value)950     public synchronized boolean remove(Object key, Object value) {
951         Objects.requireNonNull(value);
952 
953         HashtableEntry<?,?> tab[] = table;
954         int hash = key.hashCode();
955         int index = (hash & 0x7FFFFFFF) % tab.length;
956         @SuppressWarnings("unchecked")
957         HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
958         for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
959             if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
960                 if (prev != null) {
961                     prev.next = e.next;
962                 } else {
963                     tab[index] = e.next;
964                 }
965                 e.value = null; // clear for gc
966                 modCount++;
967                 count--;
968                 return true;
969             }
970         }
971         return false;
972     }
973 
974     @Override
replace(K key, V oldValue, V newValue)975     public synchronized boolean replace(K key, V oldValue, V newValue) {
976         Objects.requireNonNull(oldValue);
977         Objects.requireNonNull(newValue);
978         HashtableEntry<?,?> tab[] = table;
979         int hash = key.hashCode();
980         int index = (hash & 0x7FFFFFFF) % tab.length;
981         @SuppressWarnings("unchecked")
982         HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
983         for (; e != null; e = e.next) {
984             if ((e.hash == hash) && e.key.equals(key)) {
985                 if (e.value.equals(oldValue)) {
986                     e.value = newValue;
987                     return true;
988                 } else {
989                     return false;
990                 }
991             }
992         }
993         return false;
994     }
995 
996     @Override
replace(K key, V value)997     public synchronized V replace(K key, V value) {
998         Objects.requireNonNull(value);
999         HashtableEntry<?,?> tab[] = table;
1000         int hash = key.hashCode();
1001         int index = (hash & 0x7FFFFFFF) % tab.length;
1002         @SuppressWarnings("unchecked")
1003         HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1004         for (; e != null; e = e.next) {
1005             if ((e.hash == hash) && e.key.equals(key)) {
1006                 V oldValue = e.value;
1007                 e.value = value;
1008                 return oldValue;
1009             }
1010         }
1011         return null;
1012     }
1013 
1014     /**
1015      * {@inheritDoc}
1016      *
1017      * <p>This method will, on a best-effort basis, throw a
1018      * {@link java.util.ConcurrentModificationException} if the mapping
1019      * function modified this map during computation.
1020      *
1021      * @throws ConcurrentModificationException if it is detected that the
1022      * mapping function modified this map
1023      */
1024     @Override
computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)1025     public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
1026         Objects.requireNonNull(mappingFunction);
1027 
1028         HashtableEntry<?,?> tab[] = table;
1029         int hash = key.hashCode();
1030         int index = (hash & 0x7FFFFFFF) % tab.length;
1031         @SuppressWarnings("unchecked")
1032         HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1033         for (; e != null; e = e.next) {
1034             if (e.hash == hash && e.key.equals(key)) {
1035                 // Hashtable not accept null value
1036                 return e.value;
1037             }
1038         }
1039 
1040         int mc = modCount;
1041         V newValue = mappingFunction.apply(key);
1042         if (mc != modCount) { throw new ConcurrentModificationException(); }
1043         if (newValue != null) {
1044             addEntry(hash, key, newValue, index);
1045         }
1046 
1047         return newValue;
1048     }
1049 
1050     /**
1051      * {@inheritDoc}
1052      *
1053      * <p>This method will, on a best-effort basis, throw a
1054      * {@link java.util.ConcurrentModificationException} if the remapping
1055      * function modified this map during computation.
1056      *
1057      * @throws ConcurrentModificationException if it is detected that the
1058      * remapping function modified this map
1059      */
1060     @Override
computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)1061     public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
1062         Objects.requireNonNull(remappingFunction);
1063 
1064         HashtableEntry<?,?> tab[] = table;
1065         int hash = key.hashCode();
1066         int index = (hash & 0x7FFFFFFF) % tab.length;
1067         @SuppressWarnings("unchecked")
1068         HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1069         for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1070             if (e.hash == hash && e.key.equals(key)) {
1071                 int mc = modCount;
1072                 V newValue = remappingFunction.apply(key, e.value);
1073                 if (mc != modCount) {
1074                     throw new ConcurrentModificationException();
1075                 }
1076                 if (newValue == null) {
1077                     if (prev != null) {
1078                         prev.next = e.next;
1079                     } else {
1080                         tab[index] = e.next;
1081                     }
1082                     modCount = mc + 1;
1083                     count--;
1084                 } else {
1085                     e.value = newValue;
1086                 }
1087                 return newValue;
1088             }
1089         }
1090         return null;
1091     }
1092     /**
1093      * {@inheritDoc}
1094      *
1095      * <p>This method will, on a best-effort basis, throw a
1096      * {@link java.util.ConcurrentModificationException} if the remapping
1097      * function modified this map during computation.
1098      *
1099      * @throws ConcurrentModificationException if it is detected that the
1100      * remapping function modified this map
1101      */
1102     @Override
compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)1103     public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
1104         Objects.requireNonNull(remappingFunction);
1105 
1106         HashtableEntry<?,?> tab[] = table;
1107         int hash = key.hashCode();
1108         int index = (hash & 0x7FFFFFFF) % tab.length;
1109         @SuppressWarnings("unchecked")
1110         HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1111         for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1112             if (e.hash == hash && Objects.equals(e.key, key)) {
1113                 int mc = modCount;
1114                 V newValue = remappingFunction.apply(key, e.value);
1115                 if (mc != modCount) {
1116                     throw new ConcurrentModificationException();
1117                 }
1118                 if (newValue == null) {
1119                     if (prev != null) {
1120                         prev.next = e.next;
1121                     } else {
1122                         tab[index] = e.next;
1123                     }
1124                     modCount = mc + 1;
1125                     count--;
1126                 } else {
1127                     e.value = newValue;
1128                 }
1129                 return newValue;
1130             }
1131         }
1132 
1133         int mc = modCount;
1134         V newValue = remappingFunction.apply(key, null);
1135         if (mc != modCount) { throw new ConcurrentModificationException(); }
1136         if (newValue != null) {
1137             addEntry(hash, key, newValue, index);
1138         }
1139 
1140         return newValue;
1141     }
1142 
1143     /**
1144      * {@inheritDoc}
1145      *
1146      * <p>This method will, on a best-effort basis, throw a
1147      * {@link java.util.ConcurrentModificationException} if the remapping
1148      * function modified this map during computation.
1149      *
1150      * @throws ConcurrentModificationException if it is detected that the
1151      * remapping function modified this map
1152      */
1153     @Override
merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)1154     public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
1155         Objects.requireNonNull(remappingFunction);
1156 
1157         HashtableEntry<?,?> tab[] = table;
1158         int hash = key.hashCode();
1159         int index = (hash & 0x7FFFFFFF) % tab.length;
1160         @SuppressWarnings("unchecked")
1161         HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1162         for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1163             if (e.hash == hash && e.key.equals(key)) {
1164                 int mc = modCount;
1165                 V newValue = remappingFunction.apply(e.value, value);
1166                 if (mc != modCount) {
1167                     throw new ConcurrentModificationException();
1168                 }
1169                 if (newValue == null) {
1170                     if (prev != null) {
1171                         prev.next = e.next;
1172                     } else {
1173                         tab[index] = e.next;
1174                     }
1175                     modCount = mc + 1;
1176                     count--;
1177                 } else {
1178                     e.value = newValue;
1179                 }
1180                 return newValue;
1181             }
1182         }
1183 
1184         if (value != null) {
1185             addEntry(hash, key, value, index);
1186         }
1187 
1188         return value;
1189     }
1190 
1191     /**
1192      * Save the state of the Hashtable to a stream (i.e., serialize it).
1193      *
1194      * @serialData The <i>capacity</i> of the Hashtable (the length of the
1195      *             bucket array) is emitted (int), followed by the
1196      *             <i>size</i> of the Hashtable (the number of key-value
1197      *             mappings), followed by the key (Object) and value (Object)
1198      *             for each key-value mapping represented by the Hashtable
1199      *             The key-value mappings are emitted in no particular order.
1200      */
1201     @java.io.Serial
writeObject(java.io.ObjectOutputStream s)1202     private void writeObject(java.io.ObjectOutputStream s)
1203             throws IOException {
1204         writeHashtable(s);
1205     }
1206 
1207     /**
1208      * Perform serialization of the Hashtable to an ObjectOutputStream.
1209      * The Properties class overrides this method.
1210      */
writeHashtable(java.io.ObjectOutputStream s)1211     void writeHashtable(java.io.ObjectOutputStream s)
1212             throws IOException {
1213         HashtableEntry<Object, Object> entryStack = null;
1214 
1215         synchronized (this) {
1216             // Write out the threshold and loadFactor
1217             s.defaultWriteObject();
1218 
1219             // Write out the length and count of elements
1220             s.writeInt(table.length);
1221             s.writeInt(count);
1222 
1223             // Stack copies of the entries in the table
1224             for (HashtableEntry<?, ?> entry : table) {
1225 
1226                 while (entry != null) {
1227                     entryStack =
1228                         new HashtableEntry<>(0, entry.key, entry.value, entryStack);
1229                     entry = entry.next;
1230                 }
1231             }
1232         }
1233 
1234         // Write out the key/value objects from the stacked entries
1235         while (entryStack != null) {
1236             s.writeObject(entryStack.key);
1237             s.writeObject(entryStack.value);
1238             entryStack = entryStack.next;
1239         }
1240     }
1241 
1242     /**
1243      * Called by Properties to write out a simulated threshold and loadfactor.
1244      */
defaultWriteHashtable(java.io.ObjectOutputStream s, int length, float loadFactor)1245     final void defaultWriteHashtable(java.io.ObjectOutputStream s, int length,
1246             float loadFactor) throws IOException {
1247         this.threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
1248         this.loadFactor = loadFactor;
1249         s.defaultWriteObject();
1250     }
1251 
1252     /**
1253      * Reconstitute the Hashtable from a stream (i.e., deserialize it).
1254      */
1255     @java.io.Serial
readObject(ObjectInputStream s)1256     private void readObject(ObjectInputStream s)
1257             throws IOException, ClassNotFoundException {
1258         readHashtable(s);
1259     }
1260 
1261     /**
1262      * Perform deserialization of the Hashtable from an ObjectInputStream.
1263      * The Properties class overrides this method.
1264      */
readHashtable(ObjectInputStream s)1265     void readHashtable(ObjectInputStream s)
1266             throws IOException, ClassNotFoundException {
1267 
1268         ObjectInputStream.GetField fields = s.readFields();
1269 
1270         // Read and validate loadFactor (ignore threshold - it will be re-computed)
1271         float lf = fields.get("loadFactor", 0.75f);
1272         if (lf <= 0 || Float.isNaN(lf))
1273             throw new StreamCorruptedException("Illegal load factor: " + lf);
1274         lf = Math.clamp(lf, 0.25f, 4.0f);
1275 
1276         // Read the original length of the array and number of elements
1277         int origlength = s.readInt();
1278         int elements = s.readInt();
1279 
1280         // Validate # of elements
1281         if (elements < 0)
1282             throw new StreamCorruptedException("Illegal # of Elements: " + elements);
1283 
1284         // Clamp original length to be more than elements / loadFactor
1285         // (this is the invariant enforced with auto-growth)
1286         origlength = Math.max(origlength, (int)(elements / lf) + 1);
1287 
1288         // Compute new length with a bit of room 5% + 3 to grow but
1289         // no larger than the clamped original length.  Make the length
1290         // odd if it's large enough, this helps distribute the entries.
1291         // Guard against the length ending up zero, that's not valid.
1292         int length = (int)(elements * 1.05f / lf) + 3;
1293         if (length > elements && (length & 1) == 0)
1294             length--;
1295         length = Math.min(length, origlength);
1296 
1297         if (length < 0) { // overflow
1298             length = origlength;
1299         }
1300 
1301         // Check Map.Entry[].class since it's the nearest public type to
1302         // what we're actually creating.
1303         SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, length);
1304         Hashtable.UnsafeHolder.putLoadFactor(this, lf);
1305         table = new HashtableEntry<?,?>[length];
1306         threshold = (int)Math.min(length * lf, MAX_ARRAY_SIZE + 1);
1307         count = 0;
1308 
1309         // Read the number of elements and then all the key/value objects
1310         for (; elements > 0; elements--) {
1311             @SuppressWarnings("unchecked")
1312                 K key = (K)s.readObject();
1313             @SuppressWarnings("unchecked")
1314                 V value = (V)s.readObject();
1315             // sync is eliminated for performance
1316             reconstitutionPut(table, key, value);
1317         }
1318     }
1319 
1320     // Support for resetting final field during deserializing
1321     private static final class UnsafeHolder {
UnsafeHolder()1322         private UnsafeHolder() { throw new InternalError(); }
1323         private static final jdk.internal.misc.Unsafe unsafe
1324                 = jdk.internal.misc.Unsafe.getUnsafe();
1325         private static final long LF_OFFSET
1326                 = unsafe.objectFieldOffset(Hashtable.class, "loadFactor");
putLoadFactor(Hashtable<?, ?> table, float lf)1327         static void putLoadFactor(Hashtable<?, ?> table, float lf) {
1328             unsafe.putFloat(table, LF_OFFSET, lf);
1329         }
1330     }
1331 
1332     /**
1333      * The put method used by readObject. This is provided because put
1334      * is overridable and should not be called in readObject since the
1335      * subclass will not yet be initialized.
1336      *
1337      * <p>This differs from the regular put method in several ways. No
1338      * checking for rehashing is necessary since the number of elements
1339      * initially in the table is known. The modCount is not incremented and
1340      * there's no synchronization because we are creating a new instance.
1341      * Also, no return value is needed.
1342      */
reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value)1343     private void reconstitutionPut(HashtableEntry<?,?>[] tab, K key, V value)
1344         throws StreamCorruptedException
1345     {
1346         if (value == null) {
1347             throw new java.io.StreamCorruptedException();
1348         }
1349         // Makes sure the key is not already in the hashtable.
1350         // This should not happen in deserialized version.
1351         int hash = key.hashCode();
1352         int index = (hash & 0x7FFFFFFF) % tab.length;
1353         for (HashtableEntry<?,?> e = tab[index] ; e != null ; e = e.next) {
1354             if ((e.hash == hash) && e.key.equals(key)) {
1355                 throw new java.io.StreamCorruptedException();
1356             }
1357         }
1358         // Creates the new entry.
1359         @SuppressWarnings("unchecked")
1360             HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1361         tab[index] = new HashtableEntry<>(hash, key, value, e);
1362         count++;
1363     }
1364 
1365     /**
1366      * Hashtable bucket collision list entry
1367      */
1368     // BEGIN Android-changed: Renamed Entry -> HashtableEntry.
1369     // Code references to "HashTable.Entry" must mean Map.Entry
1370     //
1371     // This mirrors the corresponding rename of LinkedHashMap's
1372     // Entry->LinkedHashMapEntry.
1373     //
1374     // This is for source compatibility with earlier versions of Android.
1375     // Otherwise, it would hide Map.Entry which would break compilation
1376     // of code like:
1377     //
1378     // Hashtable.Entry<K, V> entry = hashtable.entrySet().iterator.next();
1379     //
1380     // To compile, that code snippet's "HashtableMap.Entry" must
1381     // mean java.util.Map.Entry which is the compile time type of
1382     // entrySet()'s elements.
1383     //
1384     private static class HashtableEntry<K,V> implements Map.Entry<K,V> {
1385     // END Android-changed: Renamed Entry -> HashtableEntry.
1386         final int hash;
1387         final K key;
1388         V value;
1389         HashtableEntry<K,V> next;
1390 
HashtableEntry(int hash, K key, V value, HashtableEntry<K,V> next)1391         protected HashtableEntry(int hash, K key, V value, HashtableEntry<K,V> next) {
1392             this.hash = hash;
1393             this.key =  key;
1394             this.value = value;
1395             this.next = next;
1396         }
1397 
1398         @SuppressWarnings("unchecked")
clone()1399         protected Object clone() {
1400             return new HashtableEntry<>(hash, key, value,
1401                                   (next==null ? null : (HashtableEntry<K,V>) next.clone()));
1402         }
1403 
1404         // Map.Entry Ops
1405 
getKey()1406         public K getKey() {
1407             return key;
1408         }
1409 
getValue()1410         public V getValue() {
1411             return value;
1412         }
1413 
setValue(V value)1414         public V setValue(V value) {
1415             if (value == null)
1416                 throw new NullPointerException();
1417 
1418             V oldValue = this.value;
1419             this.value = value;
1420             return oldValue;
1421         }
1422 
equals(Object o)1423         public boolean equals(Object o) {
1424             if (!(o instanceof Map.Entry<?, ?> e))
1425                 return false;
1426 
1427             return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
1428                (value==null ? e.getValue()==null : value.equals(e.getValue()));
1429         }
1430 
hashCode()1431         public int hashCode() {
1432             return hash ^ Objects.hashCode(value);
1433         }
1434 
toString()1435         public String toString() {
1436             return key.toString()+"="+value.toString();
1437         }
1438     }
1439 
1440     // Types of Enumerations/Iterations
1441     private static final int KEYS = 0;
1442     private static final int VALUES = 1;
1443     private static final int ENTRIES = 2;
1444 
1445     /**
1446      * A hashtable enumerator class.  This class implements both the
1447      * Enumeration and Iterator interfaces, but individual instances
1448      * can be created with the Iterator methods disabled.  This is necessary
1449      * to avoid unintentionally increasing the capabilities granted a user
1450      * by passing an Enumeration.
1451      */
1452     private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
1453         HashtableEntry<?,?>[] table = Hashtable.this.table;
1454         int index = table.length;
1455         HashtableEntry<?,?> entry;
1456         HashtableEntry<?,?> lastReturned;
1457         final int type;
1458 
1459         /**
1460          * Indicates whether this Enumerator is serving as an Iterator
1461          * or an Enumeration.  (true -> Iterator).
1462          */
1463         final boolean iterator;
1464 
1465         /**
1466          * The modCount value that the iterator believes that the backing
1467          * Hashtable should have.  If this expectation is violated, the iterator
1468          * has detected concurrent modification.
1469          */
1470         protected int expectedModCount = Hashtable.this.modCount;
1471 
Enumerator(int type, boolean iterator)1472         Enumerator(int type, boolean iterator) {
1473             this.type = type;
1474             this.iterator = iterator;
1475         }
1476 
hasMoreElements()1477         public boolean hasMoreElements() {
1478             HashtableEntry<?,?> e = entry;
1479             int i = index;
1480             HashtableEntry<?,?>[] t = table;
1481             /* Use locals for faster loop iteration */
1482             while (e == null && i > 0) {
1483                 e = t[--i];
1484             }
1485             entry = e;
1486             index = i;
1487             return e != null;
1488         }
1489 
1490         @SuppressWarnings("unchecked")
nextElement()1491         public T nextElement() {
1492             HashtableEntry<?,?> et = entry;
1493             int i = index;
1494             HashtableEntry<?,?>[] t = table;
1495             /* Use locals for faster loop iteration */
1496             while (et == null && i > 0) {
1497                 et = t[--i];
1498             }
1499             entry = et;
1500             index = i;
1501             if (et != null) {
1502                 HashtableEntry<?,?> e = lastReturned = entry;
1503                 entry = e.next;
1504                 return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
1505             }
1506             throw new NoSuchElementException("Hashtable Enumerator");
1507         }
1508 
1509         // Iterator methods
hasNext()1510         public boolean hasNext() {
1511             return hasMoreElements();
1512         }
1513 
next()1514         public T next() {
1515             if (Hashtable.this.modCount != expectedModCount)
1516                 throw new ConcurrentModificationException();
1517             return nextElement();
1518         }
1519 
remove()1520         public void remove() {
1521             if (!iterator)
1522                 throw new UnsupportedOperationException();
1523             if (lastReturned == null)
1524                 throw new IllegalStateException("Hashtable Enumerator");
1525             if (modCount != expectedModCount)
1526                 throw new ConcurrentModificationException();
1527 
1528             synchronized(Hashtable.this) {
1529                 HashtableEntry<?,?>[] tab = Hashtable.this.table;
1530                 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
1531 
1532                 @SuppressWarnings("unchecked")
1533                 HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
1534                 for(HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
1535                     if (e == lastReturned) {
1536                         if (prev == null)
1537                             tab[index] = e.next;
1538                         else
1539                             prev.next = e.next;
1540                         expectedModCount++;
1541                         lastReturned = null;
1542                         Hashtable.this.modCount++;
1543                         Hashtable.this.count--;
1544                         return;
1545                     }
1546                 }
1547                 throw new ConcurrentModificationException();
1548             }
1549         }
1550     }
1551 }
1552