Path: blob/master/src/java.base/share/classes/java/util/Hashtable.java
41152 views
/*1* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util;2627import java.io.*;28import java.util.function.BiConsumer;29import java.util.function.Function;30import java.util.function.BiFunction;31import jdk.internal.access.SharedSecrets;3233/**34* This class implements a hash table, which maps keys to values. Any35* non-{@code null} object can be used as a key or as a value. <p>36*37* To successfully store and retrieve objects from a hashtable, the38* objects used as keys must implement the {@code hashCode}39* method and the {@code equals} method. <p>40*41* An instance of {@code Hashtable} has two parameters that affect its42* performance: <i>initial capacity</i> and <i>load factor</i>. The43* <i>capacity</i> is the number of <i>buckets</i> in the hash table, and the44* <i>initial capacity</i> is simply the capacity at the time the hash table45* is created. Note that the hash table is <i>open</i>: in the case of a "hash46* collision", a single bucket stores multiple entries, which must be searched47* sequentially. The <i>load factor</i> is a measure of how full the hash48* table is allowed to get before its capacity is automatically increased.49* The initial capacity and load factor parameters are merely hints to50* the implementation. The exact details as to when and whether the rehash51* method is invoked are implementation-dependent.<p>52*53* Generally, the default load factor (.75) offers a good tradeoff between54* time and space costs. Higher values decrease the space overhead but55* increase the time cost to look up an entry (which is reflected in most56* {@code Hashtable} operations, including {@code get} and {@code put}).<p>57*58* The initial capacity controls a tradeoff between wasted space and the59* need for {@code rehash} operations, which are time-consuming.60* No {@code rehash} operations will <i>ever</i> occur if the initial61* capacity is greater than the maximum number of entries the62* {@code Hashtable} will contain divided by its load factor. However,63* setting the initial capacity too high can waste space.<p>64*65* If many entries are to be made into a {@code Hashtable},66* creating it with a sufficiently large capacity may allow the67* entries to be inserted more efficiently than letting it perform68* automatic rehashing as needed to grow the table. <p>69*70* This example creates a hashtable of numbers. It uses the names of71* the numbers as keys:72* <pre> {@code73* Hashtable<String, Integer> numbers74* = new Hashtable<String, Integer>();75* numbers.put("one", 1);76* numbers.put("two", 2);77* numbers.put("three", 3);}</pre>78*79* <p>To retrieve a number, use the following code:80* <pre> {@code81* Integer n = numbers.get("two");82* if (n != null) {83* System.out.println("two = " + n);84* }}</pre>85*86* <p>The iterators returned by the {@code iterator} method of the collections87* returned by all of this class's "collection view methods" are88* <em>fail-fast</em>: if the Hashtable is structurally modified at any time89* after the iterator is created, in any way except through the iterator's own90* {@code remove} method, the iterator will throw a {@link91* ConcurrentModificationException}. Thus, in the face of concurrent92* modification, the iterator fails quickly and cleanly, rather than risking93* arbitrary, non-deterministic behavior at an undetermined time in the future.94* The Enumerations returned by Hashtable's {@link #keys keys} and95* {@link #elements elements} methods are <em>not</em> fail-fast; if the96* Hashtable is structurally modified at any time after the enumeration is97* created then the results of enumerating are undefined.98*99* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed100* as it is, generally speaking, impossible to make any hard guarantees in the101* presence of unsynchronized concurrent modification. Fail-fast iterators102* throw {@code ConcurrentModificationException} on a best-effort basis.103* Therefore, it would be wrong to write a program that depended on this104* exception for its correctness: <i>the fail-fast behavior of iterators105* should be used only to detect bugs.</i>106*107* <p>As of the Java 2 platform v1.2, this class was retrofitted to108* implement the {@link Map} interface, making it a member of the109* <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">110*111* Java Collections Framework</a>. Unlike the new collection112* implementations, {@code Hashtable} is synchronized. If a113* thread-safe implementation is not needed, it is recommended to use114* {@link HashMap} in place of {@code Hashtable}. If a thread-safe115* highly-concurrent implementation is desired, then it is recommended116* to use {@link java.util.concurrent.ConcurrentHashMap} in place of117* {@code Hashtable}.118*119* @param <K> the type of keys maintained by this map120* @param <V> the type of mapped values121*122* @author Arthur van Hoff123* @author Josh Bloch124* @author Neal Gafter125* @see Object#equals(java.lang.Object)126* @see Object#hashCode()127* @see Hashtable#rehash()128* @see Collection129* @see Map130* @see HashMap131* @see TreeMap132* @since 1.0133*/134public class Hashtable<K,V>135extends Dictionary<K,V>136implements Map<K,V>, Cloneable, java.io.Serializable {137138/**139* The hash table data.140*/141private transient Entry<?,?>[] table;142143/**144* The total number of entries in the hash table.145*/146private transient int count;147148/**149* The table is rehashed when its size exceeds this threshold. (The150* value of this field is (int)(capacity * loadFactor).)151*152* @serial153*/154private int threshold;155156/**157* The load factor for the hashtable.158*159* @serial160*/161private float loadFactor;162163/**164* The number of times this Hashtable has been structurally modified165* Structural modifications are those that change the number of entries in166* the Hashtable or otherwise modify its internal structure (e.g.,167* rehash). This field is used to make iterators on Collection-views of168* the Hashtable fail-fast. (See ConcurrentModificationException).169*/170private transient int modCount = 0;171172/** use serialVersionUID from JDK 1.0.2 for interoperability */173@java.io.Serial174private static final long serialVersionUID = 1421746759512286392L;175176/**177* Constructs a new, empty hashtable with the specified initial178* capacity and the specified load factor.179*180* @param initialCapacity the initial capacity of the hashtable.181* @param loadFactor the load factor of the hashtable.182* @throws IllegalArgumentException if the initial capacity is less183* than zero, or if the load factor is nonpositive.184*/185public Hashtable(int initialCapacity, float loadFactor) {186if (initialCapacity < 0)187throw new IllegalArgumentException("Illegal Capacity: "+188initialCapacity);189if (loadFactor <= 0 || Float.isNaN(loadFactor))190throw new IllegalArgumentException("Illegal Load: "+loadFactor);191192if (initialCapacity==0)193initialCapacity = 1;194this.loadFactor = loadFactor;195table = new Entry<?,?>[initialCapacity];196threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);197}198199/**200* Constructs a new, empty hashtable with the specified initial capacity201* and default load factor (0.75).202*203* @param initialCapacity the initial capacity of the hashtable.204* @throws IllegalArgumentException if the initial capacity is less205* than zero.206*/207public Hashtable(int initialCapacity) {208this(initialCapacity, 0.75f);209}210211/**212* Constructs a new, empty hashtable with a default initial capacity (11)213* and load factor (0.75).214*/215public Hashtable() {216this(11, 0.75f);217}218219/**220* Constructs a new hashtable with the same mappings as the given221* Map. The hashtable is created with an initial capacity sufficient to222* hold the mappings in the given Map and a default load factor (0.75).223*224* @param t the map whose mappings are to be placed in this map.225* @throws NullPointerException if the specified map is null.226* @since 1.2227*/228public Hashtable(Map<? extends K, ? extends V> t) {229this(Math.max(2*t.size(), 11), 0.75f);230putAll(t);231}232233/**234* A constructor chained from {@link Properties} keeps Hashtable fields235* uninitialized since they are not used.236*237* @param dummy a dummy parameter238*/239Hashtable(Void dummy) {}240241/**242* Returns the number of keys in this hashtable.243*244* @return the number of keys in this hashtable.245*/246public synchronized int size() {247return count;248}249250/**251* Tests if this hashtable maps no keys to values.252*253* @return {@code true} if this hashtable maps no keys to values;254* {@code false} otherwise.255*/256public synchronized boolean isEmpty() {257return count == 0;258}259260/**261* Returns an enumeration of the keys in this hashtable.262* Use the Enumeration methods on the returned object to fetch the keys263* sequentially. If the hashtable is structurally modified while enumerating264* over the keys then the results of enumerating are undefined.265*266* @return an enumeration of the keys in this hashtable.267* @see Enumeration268* @see #elements()269* @see #keySet()270* @see Map271*/272public synchronized Enumeration<K> keys() {273return this.<K>getEnumeration(KEYS);274}275276/**277* Returns an enumeration of the values in this hashtable.278* Use the Enumeration methods on the returned object to fetch the elements279* sequentially. If the hashtable is structurally modified while enumerating280* over the values then the results of enumerating are undefined.281*282* @return an enumeration of the values in this hashtable.283* @see java.util.Enumeration284* @see #keys()285* @see #values()286* @see Map287*/288public synchronized Enumeration<V> elements() {289return this.<V>getEnumeration(VALUES);290}291292/**293* Tests if some key maps into the specified value in this hashtable.294* This operation is more expensive than the {@link #containsKey295* containsKey} method.296*297* <p>Note that this method is identical in functionality to298* {@link #containsValue containsValue}, (which is part of the299* {@link Map} interface in the collections framework).300*301* @param value a value to search for302* @return {@code true} if and only if some key maps to the303* {@code value} argument in this hashtable as304* determined by the {@code equals} method;305* {@code false} otherwise.306* @throws NullPointerException if the value is {@code null}307*/308public synchronized boolean contains(Object value) {309if (value == null) {310throw new NullPointerException();311}312313Entry<?,?> tab[] = table;314for (int i = tab.length ; i-- > 0 ;) {315for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {316if (e.value.equals(value)) {317return true;318}319}320}321return false;322}323324/**325* Returns true if this hashtable maps one or more keys to this value.326*327* <p>Note that this method is identical in functionality to {@link328* #contains contains} (which predates the {@link Map} interface).329*330* @param value value whose presence in this hashtable is to be tested331* @return {@code true} if this map maps one or more keys to the332* specified value333* @throws NullPointerException if the value is {@code null}334* @since 1.2335*/336public boolean containsValue(Object value) {337return contains(value);338}339340/**341* Tests if the specified object is a key in this hashtable.342*343* @param key possible key344* @return {@code true} if and only if the specified object345* is a key in this hashtable, as determined by the346* {@code equals} method; {@code false} otherwise.347* @throws NullPointerException if the key is {@code null}348* @see #contains(Object)349*/350public synchronized boolean containsKey(Object key) {351Entry<?,?> tab[] = table;352int hash = key.hashCode();353int index = (hash & 0x7FFFFFFF) % tab.length;354for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {355if ((e.hash == hash) && e.key.equals(key)) {356return true;357}358}359return false;360}361362/**363* Returns the value to which the specified key is mapped,364* or {@code null} if this map contains no mapping for the key.365*366* <p>More formally, if this map contains a mapping from a key367* {@code k} to a value {@code v} such that {@code (key.equals(k))},368* then this method returns {@code v}; otherwise it returns369* {@code null}. (There can be at most one such mapping.)370*371* @param key the key whose associated value is to be returned372* @return the value to which the specified key is mapped, or373* {@code null} if this map contains no mapping for the key374* @throws NullPointerException if the specified key is null375* @see #put(Object, Object)376*/377@SuppressWarnings("unchecked")378public synchronized V get(Object key) {379Entry<?,?> tab[] = table;380int hash = key.hashCode();381int index = (hash & 0x7FFFFFFF) % tab.length;382for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {383if ((e.hash == hash) && e.key.equals(key)) {384return (V)e.value;385}386}387return null;388}389390/**391* The maximum size of array to allocate.392* Some VMs reserve some header words in an array.393* Attempts to allocate larger arrays may result in394* OutOfMemoryError: Requested array size exceeds VM limit395*/396private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;397398/**399* Increases the capacity of and internally reorganizes this400* hashtable, in order to accommodate and access its entries more401* efficiently. This method is called automatically when the402* number of keys in the hashtable exceeds this hashtable's capacity403* and load factor.404*/405@SuppressWarnings("unchecked")406protected void rehash() {407int oldCapacity = table.length;408Entry<?,?>[] oldMap = table;409410// overflow-conscious code411int newCapacity = (oldCapacity << 1) + 1;412if (newCapacity - MAX_ARRAY_SIZE > 0) {413if (oldCapacity == MAX_ARRAY_SIZE)414// Keep running with MAX_ARRAY_SIZE buckets415return;416newCapacity = MAX_ARRAY_SIZE;417}418Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];419420modCount++;421threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);422table = newMap;423424for (int i = oldCapacity ; i-- > 0 ;) {425for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {426Entry<K,V> e = old;427old = old.next;428429int index = (e.hash & 0x7FFFFFFF) % newCapacity;430e.next = (Entry<K,V>)newMap[index];431newMap[index] = e;432}433}434}435436private void addEntry(int hash, K key, V value, int index) {437Entry<?,?> tab[] = table;438if (count >= threshold) {439// Rehash the table if the threshold is exceeded440rehash();441442tab = table;443hash = key.hashCode();444index = (hash & 0x7FFFFFFF) % tab.length;445}446447// Creates the new entry.448@SuppressWarnings("unchecked")449Entry<K,V> e = (Entry<K,V>) tab[index];450tab[index] = new Entry<>(hash, key, value, e);451count++;452modCount++;453}454455/**456* Maps the specified {@code key} to the specified457* {@code value} in this hashtable. Neither the key nor the458* value can be {@code null}. <p>459*460* The value can be retrieved by calling the {@code get} method461* with a key that is equal to the original key.462*463* @param key the hashtable key464* @param value the value465* @return the previous value of the specified key in this hashtable,466* or {@code null} if it did not have one467* @throws NullPointerException if the key or value is468* {@code null}469* @see Object#equals(Object)470* @see #get(Object)471*/472public synchronized V put(K key, V value) {473// Make sure the value is not null474if (value == null) {475throw new NullPointerException();476}477478// Makes sure the key is not already in the hashtable.479Entry<?,?> tab[] = table;480int hash = key.hashCode();481int index = (hash & 0x7FFFFFFF) % tab.length;482@SuppressWarnings("unchecked")483Entry<K,V> entry = (Entry<K,V>)tab[index];484for(; entry != null ; entry = entry.next) {485if ((entry.hash == hash) && entry.key.equals(key)) {486V old = entry.value;487entry.value = value;488return old;489}490}491492addEntry(hash, key, value, index);493return null;494}495496/**497* Removes the key (and its corresponding value) from this498* hashtable. This method does nothing if the key is not in the hashtable.499*500* @param key the key that needs to be removed501* @return the value to which the key had been mapped in this hashtable,502* or {@code null} if the key did not have a mapping503* @throws NullPointerException if the key is {@code null}504*/505public synchronized V remove(Object key) {506Entry<?,?> tab[] = table;507int hash = key.hashCode();508int index = (hash & 0x7FFFFFFF) % tab.length;509@SuppressWarnings("unchecked")510Entry<K,V> e = (Entry<K,V>)tab[index];511for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {512if ((e.hash == hash) && e.key.equals(key)) {513if (prev != null) {514prev.next = e.next;515} else {516tab[index] = e.next;517}518modCount++;519count--;520V oldValue = e.value;521e.value = null;522return oldValue;523}524}525return null;526}527528/**529* Copies all of the mappings from the specified map to this hashtable.530* These mappings will replace any mappings that this hashtable had for any531* of the keys currently in the specified map.532*533* @param t mappings to be stored in this map534* @throws NullPointerException if the specified map is null535* @since 1.2536*/537public synchronized void putAll(Map<? extends K, ? extends V> t) {538for (Map.Entry<? extends K, ? extends V> e : t.entrySet())539put(e.getKey(), e.getValue());540}541542/**543* Clears this hashtable so that it contains no keys.544*/545public synchronized void clear() {546Entry<?,?> tab[] = table;547for (int index = tab.length; --index >= 0; )548tab[index] = null;549modCount++;550count = 0;551}552553/**554* Creates a shallow copy of this hashtable. All the structure of the555* hashtable itself is copied, but the keys and values are not cloned.556* This is a relatively expensive operation.557*558* @return a clone of the hashtable559*/560public synchronized Object clone() {561Hashtable<?,?> t = cloneHashtable();562t.table = new Entry<?,?>[table.length];563for (int i = table.length ; i-- > 0 ; ) {564t.table[i] = (table[i] != null)565? (Entry<?,?>) table[i].clone() : null;566}567t.keySet = null;568t.entrySet = null;569t.values = null;570t.modCount = 0;571return t;572}573574/** Calls super.clone() */575final Hashtable<?,?> cloneHashtable() {576try {577return (Hashtable<?,?>)super.clone();578} catch (CloneNotSupportedException e) {579// this shouldn't happen, since we are Cloneable580throw new InternalError(e);581}582}583584/**585* Returns a string representation of this {@code Hashtable} object586* in the form of a set of entries, enclosed in braces and separated587* by the ASCII characters "<code> , </code>" (comma and space). Each588* entry is rendered as the key, an equals sign {@code =}, and the589* associated element, where the {@code toString} method is used to590* convert the key and element to strings.591*592* @return a string representation of this hashtable593*/594public synchronized String toString() {595int max = size() - 1;596if (max == -1)597return "{}";598599StringBuilder sb = new StringBuilder();600Iterator<Map.Entry<K,V>> it = entrySet().iterator();601602sb.append('{');603for (int i = 0; ; i++) {604Map.Entry<K,V> e = it.next();605K key = e.getKey();606V value = e.getValue();607sb.append(key == this ? "(this Map)" : key.toString());608sb.append('=');609sb.append(value == this ? "(this Map)" : value.toString());610611if (i == max)612return sb.append('}').toString();613sb.append(", ");614}615}616617618private <T> Enumeration<T> getEnumeration(int type) {619if (count == 0) {620return Collections.emptyEnumeration();621} else {622return new Enumerator<>(type, false);623}624}625626private <T> Iterator<T> getIterator(int type) {627if (count == 0) {628return Collections.emptyIterator();629} else {630return new Enumerator<>(type, true);631}632}633634// Views635636/**637* Each of these fields are initialized to contain an instance of the638* appropriate view the first time this view is requested. The views are639* stateless, so there's no reason to create more than one of each.640*/641private transient volatile Set<K> keySet;642private transient volatile Set<Map.Entry<K,V>> entrySet;643private transient volatile Collection<V> values;644645/**646* Returns a {@link Set} view of the keys contained in this map.647* The set is backed by the map, so changes to the map are648* reflected in the set, and vice-versa. If the map is modified649* while an iteration over the set is in progress (except through650* the iterator's own {@code remove} operation), the results of651* the iteration are undefined. The set supports element removal,652* which removes the corresponding mapping from the map, via the653* {@code Iterator.remove}, {@code Set.remove},654* {@code removeAll}, {@code retainAll}, and {@code clear}655* operations. It does not support the {@code add} or {@code addAll}656* operations.657*658* @since 1.2659*/660public Set<K> keySet() {661if (keySet == null)662keySet = Collections.synchronizedSet(new KeySet(), this);663return keySet;664}665666private class KeySet extends AbstractSet<K> {667public Iterator<K> iterator() {668return getIterator(KEYS);669}670public int size() {671return count;672}673public boolean contains(Object o) {674return containsKey(o);675}676public boolean remove(Object o) {677return Hashtable.this.remove(o) != null;678}679public void clear() {680Hashtable.this.clear();681}682}683684/**685* Returns a {@link Set} view of the mappings contained in this map.686* The set is backed by the map, so changes to the map are687* reflected in the set, and vice-versa. If the map is modified688* while an iteration over the set is in progress (except through689* the iterator's own {@code remove} operation, or through the690* {@code setValue} operation on a map entry returned by the691* iterator) the results of the iteration are undefined. The set692* supports element removal, which removes the corresponding693* mapping from the map, via the {@code Iterator.remove},694* {@code Set.remove}, {@code removeAll}, {@code retainAll} and695* {@code clear} operations. It does not support the696* {@code add} or {@code addAll} operations.697*698* @since 1.2699*/700public Set<Map.Entry<K,V>> entrySet() {701if (entrySet==null)702entrySet = Collections.synchronizedSet(new EntrySet(), this);703return entrySet;704}705706private class EntrySet extends AbstractSet<Map.Entry<K,V>> {707public Iterator<Map.Entry<K,V>> iterator() {708return getIterator(ENTRIES);709}710711public boolean add(Map.Entry<K,V> o) {712return super.add(o);713}714715public boolean contains(Object o) {716if (!(o instanceof Map.Entry<?, ?> entry))717return false;718Object key = entry.getKey();719Entry<?,?>[] tab = table;720int hash = key.hashCode();721int index = (hash & 0x7FFFFFFF) % tab.length;722723for (Entry<?,?> e = tab[index]; e != null; e = e.next)724if (e.hash==hash && e.equals(entry))725return true;726return false;727}728729public boolean remove(Object o) {730if (!(o instanceof Map.Entry<?, ?> entry))731return false;732Object key = entry.getKey();733Entry<?,?>[] tab = table;734int hash = key.hashCode();735int index = (hash & 0x7FFFFFFF) % tab.length;736737@SuppressWarnings("unchecked")738Entry<K,V> e = (Entry<K,V>)tab[index];739for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {740if (e.hash==hash && e.equals(entry)) {741if (prev != null)742prev.next = e.next;743else744tab[index] = e.next;745746e.value = null; // clear for gc.747modCount++;748count--;749return true;750}751}752return false;753}754755public int size() {756return count;757}758759public void clear() {760Hashtable.this.clear();761}762}763764/**765* Returns a {@link Collection} view of the values contained in this map.766* The collection is backed by the map, so changes to the map are767* reflected in the collection, and vice-versa. If the map is768* modified while an iteration over the collection is in progress769* (except through the iterator's own {@code remove} operation),770* the results of the iteration are undefined. The collection771* supports element removal, which removes the corresponding772* mapping from the map, via the {@code Iterator.remove},773* {@code Collection.remove}, {@code removeAll},774* {@code retainAll} and {@code clear} operations. It does not775* support the {@code add} or {@code addAll} operations.776*777* @since 1.2778*/779public Collection<V> values() {780if (values==null)781values = Collections.synchronizedCollection(new ValueCollection(),782this);783return values;784}785786private class ValueCollection extends AbstractCollection<V> {787public Iterator<V> iterator() {788return getIterator(VALUES);789}790public int size() {791return count;792}793public boolean contains(Object o) {794return containsValue(o);795}796public void clear() {797Hashtable.this.clear();798}799}800801// Comparison and hashing802803/**804* Compares the specified Object with this Map for equality,805* as per the definition in the Map interface.806*807* @param o object to be compared for equality with this hashtable808* @return true if the specified Object is equal to this Map809* @see Map#equals(Object)810* @since 1.2811*/812public synchronized boolean equals(Object o) {813if (o == this)814return true;815816if (!(o instanceof Map<?, ?> t))817return false;818if (t.size() != size())819return false;820821try {822for (Map.Entry<K, V> e : entrySet()) {823K key = e.getKey();824V value = e.getValue();825if (value == null) {826if (!(t.get(key) == null && t.containsKey(key)))827return false;828} else {829if (!value.equals(t.get(key)))830return false;831}832}833} catch (ClassCastException unused) {834return false;835} catch (NullPointerException unused) {836return false;837}838839return true;840}841842/**843* Returns the hash code value for this Map as per the definition in the844* Map interface.845*846* @see Map#hashCode()847* @since 1.2848*/849public synchronized int hashCode() {850/*851* This code detects the recursion caused by computing the hash code852* of a self-referential hash table and prevents the stack overflow853* that would otherwise result. This allows certain 1.1-era854* applets with self-referential hash tables to work. This code855* abuses the loadFactor field to do double-duty as a hashCode856* in progress flag, so as not to worsen the space performance.857* A negative load factor indicates that hash code computation is858* in progress.859*/860int h = 0;861if (count == 0 || loadFactor < 0)862return h; // Returns zero863864loadFactor = -loadFactor; // Mark hashCode computation in progress865Entry<?,?>[] tab = table;866for (Entry<?,?> entry : tab) {867while (entry != null) {868h += entry.hashCode();869entry = entry.next;870}871}872873loadFactor = -loadFactor; // Mark hashCode computation complete874875return h;876}877878@Override879public synchronized V getOrDefault(Object key, V defaultValue) {880V result = get(key);881return (null == result) ? defaultValue : result;882}883884@SuppressWarnings("unchecked")885@Override886public synchronized void forEach(BiConsumer<? super K, ? super V> action) {887Objects.requireNonNull(action); // explicit check required in case888// table is empty.889final int expectedModCount = modCount;890891Entry<?, ?>[] tab = table;892for (Entry<?, ?> entry : tab) {893while (entry != null) {894action.accept((K)entry.key, (V)entry.value);895entry = entry.next;896897if (expectedModCount != modCount) {898throw new ConcurrentModificationException();899}900}901}902}903904@SuppressWarnings("unchecked")905@Override906public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {907Objects.requireNonNull(function); // explicit check required in case908// table is empty.909final int expectedModCount = modCount;910911Entry<K, V>[] tab = (Entry<K, V>[])table;912for (Entry<K, V> entry : tab) {913while (entry != null) {914entry.value = Objects.requireNonNull(915function.apply(entry.key, entry.value));916entry = entry.next;917918if (expectedModCount != modCount) {919throw new ConcurrentModificationException();920}921}922}923}924925@Override926public synchronized V putIfAbsent(K key, V value) {927Objects.requireNonNull(value);928929// Makes sure the key is not already in the hashtable.930Entry<?,?> tab[] = table;931int hash = key.hashCode();932int index = (hash & 0x7FFFFFFF) % tab.length;933@SuppressWarnings("unchecked")934Entry<K,V> entry = (Entry<K,V>)tab[index];935for (; entry != null; entry = entry.next) {936if ((entry.hash == hash) && entry.key.equals(key)) {937V old = entry.value;938if (old == null) {939entry.value = value;940}941return old;942}943}944945addEntry(hash, key, value, index);946return null;947}948949@Override950public synchronized boolean remove(Object key, Object value) {951Objects.requireNonNull(value);952953Entry<?,?> tab[] = table;954int hash = key.hashCode();955int index = (hash & 0x7FFFFFFF) % tab.length;956@SuppressWarnings("unchecked")957Entry<K,V> e = (Entry<K,V>)tab[index];958for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {959if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {960if (prev != null) {961prev.next = e.next;962} else {963tab[index] = e.next;964}965e.value = null; // clear for gc966modCount++;967count--;968return true;969}970}971return false;972}973974@Override975public synchronized boolean replace(K key, V oldValue, V newValue) {976Objects.requireNonNull(oldValue);977Objects.requireNonNull(newValue);978Entry<?,?> tab[] = table;979int hash = key.hashCode();980int index = (hash & 0x7FFFFFFF) % tab.length;981@SuppressWarnings("unchecked")982Entry<K,V> e = (Entry<K,V>)tab[index];983for (; e != null; e = e.next) {984if ((e.hash == hash) && e.key.equals(key)) {985if (e.value.equals(oldValue)) {986e.value = newValue;987return true;988} else {989return false;990}991}992}993return false;994}995996@Override997public synchronized V replace(K key, V value) {998Objects.requireNonNull(value);999Entry<?,?> tab[] = table;1000int hash = key.hashCode();1001int index = (hash & 0x7FFFFFFF) % tab.length;1002@SuppressWarnings("unchecked")1003Entry<K,V> e = (Entry<K,V>)tab[index];1004for (; e != null; e = e.next) {1005if ((e.hash == hash) && e.key.equals(key)) {1006V oldValue = e.value;1007e.value = value;1008return oldValue;1009}1010}1011return null;1012}10131014/**1015* {@inheritDoc}1016*1017* <p>This method will, on a best-effort basis, throw a1018* {@link java.util.ConcurrentModificationException} if the mapping1019* function modified this map during computation.1020*1021* @throws ConcurrentModificationException if it is detected that the1022* mapping function modified this map1023*/1024@Override1025public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {1026Objects.requireNonNull(mappingFunction);10271028Entry<?,?> tab[] = table;1029int hash = key.hashCode();1030int index = (hash & 0x7FFFFFFF) % tab.length;1031@SuppressWarnings("unchecked")1032Entry<K,V> e = (Entry<K,V>)tab[index];1033for (; e != null; e = e.next) {1034if (e.hash == hash && e.key.equals(key)) {1035// Hashtable not accept null value1036return e.value;1037}1038}10391040int mc = modCount;1041V newValue = mappingFunction.apply(key);1042if (mc != modCount) { throw new ConcurrentModificationException(); }1043if (newValue != null) {1044addEntry(hash, key, newValue, index);1045}10461047return newValue;1048}10491050/**1051* {@inheritDoc}1052*1053* <p>This method will, on a best-effort basis, throw a1054* {@link java.util.ConcurrentModificationException} if the remapping1055* function modified this map during computation.1056*1057* @throws ConcurrentModificationException if it is detected that the1058* remapping function modified this map1059*/1060@Override1061public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {1062Objects.requireNonNull(remappingFunction);10631064Entry<?,?> tab[] = table;1065int hash = key.hashCode();1066int index = (hash & 0x7FFFFFFF) % tab.length;1067@SuppressWarnings("unchecked")1068Entry<K,V> e = (Entry<K,V>)tab[index];1069for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {1070if (e.hash == hash && e.key.equals(key)) {1071int mc = modCount;1072V newValue = remappingFunction.apply(key, e.value);1073if (mc != modCount) {1074throw new ConcurrentModificationException();1075}1076if (newValue == null) {1077if (prev != null) {1078prev.next = e.next;1079} else {1080tab[index] = e.next;1081}1082modCount = mc + 1;1083count--;1084} else {1085e.value = newValue;1086}1087return newValue;1088}1089}1090return null;1091}1092/**1093* {@inheritDoc}1094*1095* <p>This method will, on a best-effort basis, throw a1096* {@link java.util.ConcurrentModificationException} if the remapping1097* function modified this map during computation.1098*1099* @throws ConcurrentModificationException if it is detected that the1100* remapping function modified this map1101*/1102@Override1103public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {1104Objects.requireNonNull(remappingFunction);11051106Entry<?,?> tab[] = table;1107int hash = key.hashCode();1108int index = (hash & 0x7FFFFFFF) % tab.length;1109@SuppressWarnings("unchecked")1110Entry<K,V> e = (Entry<K,V>)tab[index];1111for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {1112if (e.hash == hash && Objects.equals(e.key, key)) {1113int mc = modCount;1114V newValue = remappingFunction.apply(key, e.value);1115if (mc != modCount) {1116throw new ConcurrentModificationException();1117}1118if (newValue == null) {1119if (prev != null) {1120prev.next = e.next;1121} else {1122tab[index] = e.next;1123}1124modCount = mc + 1;1125count--;1126} else {1127e.value = newValue;1128}1129return newValue;1130}1131}11321133int mc = modCount;1134V newValue = remappingFunction.apply(key, null);1135if (mc != modCount) { throw new ConcurrentModificationException(); }1136if (newValue != null) {1137addEntry(hash, key, newValue, index);1138}11391140return newValue;1141}11421143/**1144* {@inheritDoc}1145*1146* <p>This method will, on a best-effort basis, throw a1147* {@link java.util.ConcurrentModificationException} if the remapping1148* function modified this map during computation.1149*1150* @throws ConcurrentModificationException if it is detected that the1151* remapping function modified this map1152*/1153@Override1154public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {1155Objects.requireNonNull(remappingFunction);11561157Entry<?,?> tab[] = table;1158int hash = key.hashCode();1159int index = (hash & 0x7FFFFFFF) % tab.length;1160@SuppressWarnings("unchecked")1161Entry<K,V> e = (Entry<K,V>)tab[index];1162for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {1163if (e.hash == hash && e.key.equals(key)) {1164int mc = modCount;1165V newValue = remappingFunction.apply(e.value, value);1166if (mc != modCount) {1167throw new ConcurrentModificationException();1168}1169if (newValue == null) {1170if (prev != null) {1171prev.next = e.next;1172} else {1173tab[index] = e.next;1174}1175modCount = mc + 1;1176count--;1177} else {1178e.value = newValue;1179}1180return newValue;1181}1182}11831184if (value != null) {1185addEntry(hash, key, value, index);1186}11871188return value;1189}11901191/**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 the1195* bucket array) is emitted (int), followed by the1196* <i>size</i> of the Hashtable (the number of key-value1197* mappings), followed by the key (Object) and value (Object)1198* for each key-value mapping represented by the Hashtable1199* The key-value mappings are emitted in no particular order.1200*/1201@java.io.Serial1202private void writeObject(java.io.ObjectOutputStream s)1203throws IOException {1204writeHashtable(s);1205}12061207/**1208* Perform serialization of the Hashtable to an ObjectOutputStream.1209* The Properties class overrides this method.1210*/1211void writeHashtable(java.io.ObjectOutputStream s)1212throws IOException {1213Entry<Object, Object> entryStack = null;12141215synchronized (this) {1216// Write out the threshold and loadFactor1217s.defaultWriteObject();12181219// Write out the length and count of elements1220s.writeInt(table.length);1221s.writeInt(count);12221223// Stack copies of the entries in the table1224for (Entry<?, ?> entry : table) {12251226while (entry != null) {1227entryStack =1228new Entry<>(0, entry.key, entry.value, entryStack);1229entry = entry.next;1230}1231}1232}12331234// Write out the key/value objects from the stacked entries1235while (entryStack != null) {1236s.writeObject(entryStack.key);1237s.writeObject(entryStack.value);1238entryStack = entryStack.next;1239}1240}12411242/**1243* Called by Properties to write out a simulated threshold and loadfactor.1244*/1245final void defaultWriteHashtable(java.io.ObjectOutputStream s, int length,1246float loadFactor) throws IOException {1247this.threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);1248this.loadFactor = loadFactor;1249s.defaultWriteObject();1250}12511252/**1253* Reconstitute the Hashtable from a stream (i.e., deserialize it).1254*/1255@java.io.Serial1256private void readObject(java.io.ObjectInputStream s)1257throws IOException, ClassNotFoundException {1258readHashtable(s);1259}12601261/**1262* Perform deserialization of the Hashtable from an ObjectInputStream.1263* The Properties class overrides this method.1264*/1265void readHashtable(java.io.ObjectInputStream s)1266throws IOException, ClassNotFoundException {1267// Read in the threshold and loadFactor1268s.defaultReadObject();12691270// Validate loadFactor (ignore threshold - it will be re-computed)1271if (loadFactor <= 0 || Float.isNaN(loadFactor))1272throw new StreamCorruptedException("Illegal Load: " + loadFactor);12731274// Read the original length of the array and number of elements1275int origlength = s.readInt();1276int elements = s.readInt();12771278// Validate # of elements1279if (elements < 0)1280throw new StreamCorruptedException("Illegal # of Elements: " + elements);12811282// Clamp original length to be more than elements / loadFactor1283// (this is the invariant enforced with auto-growth)1284origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);12851286// Compute new length with a bit of room 5% + 3 to grow but1287// no larger than the clamped original length. Make the length1288// odd if it's large enough, this helps distribute the entries.1289// Guard against the length ending up zero, that's not valid.1290int length = (int)((elements + elements / 20) / loadFactor) + 3;1291if (length > elements && (length & 1) == 0)1292length--;1293length = Math.min(length, origlength);12941295if (length < 0) { // overflow1296length = origlength;1297}12981299// Check Map.Entry[].class since it's the nearest public type to1300// what we're actually creating.1301SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, length);1302table = new Entry<?,?>[length];1303threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);1304count = 0;13051306// Read the number of elements and then all the key/value objects1307for (; elements > 0; elements--) {1308@SuppressWarnings("unchecked")1309K key = (K)s.readObject();1310@SuppressWarnings("unchecked")1311V value = (V)s.readObject();1312// sync is eliminated for performance1313reconstitutionPut(table, key, value);1314}1315}13161317/**1318* The put method used by readObject. This is provided because put1319* is overridable and should not be called in readObject since the1320* subclass will not yet be initialized.1321*1322* <p>This differs from the regular put method in several ways. No1323* checking for rehashing is necessary since the number of elements1324* initially in the table is known. The modCount is not incremented and1325* there's no synchronization because we are creating a new instance.1326* Also, no return value is needed.1327*/1328private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)1329throws StreamCorruptedException1330{1331if (value == null) {1332throw new java.io.StreamCorruptedException();1333}1334// Makes sure the key is not already in the hashtable.1335// This should not happen in deserialized version.1336int hash = key.hashCode();1337int index = (hash & 0x7FFFFFFF) % tab.length;1338for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {1339if ((e.hash == hash) && e.key.equals(key)) {1340throw new java.io.StreamCorruptedException();1341}1342}1343// Creates the new entry.1344@SuppressWarnings("unchecked")1345Entry<K,V> e = (Entry<K,V>)tab[index];1346tab[index] = new Entry<>(hash, key, value, e);1347count++;1348}13491350/**1351* Hashtable bucket collision list entry1352*/1353private static class Entry<K,V> implements Map.Entry<K,V> {1354final int hash;1355final K key;1356V value;1357Entry<K,V> next;13581359protected Entry(int hash, K key, V value, Entry<K,V> next) {1360this.hash = hash;1361this.key = key;1362this.value = value;1363this.next = next;1364}13651366@SuppressWarnings("unchecked")1367protected Object clone() {1368return new Entry<>(hash, key, value,1369(next==null ? null : (Entry<K,V>) next.clone()));1370}13711372// Map.Entry Ops13731374public K getKey() {1375return key;1376}13771378public V getValue() {1379return value;1380}13811382public V setValue(V value) {1383if (value == null)1384throw new NullPointerException();13851386V oldValue = this.value;1387this.value = value;1388return oldValue;1389}13901391public boolean equals(Object o) {1392if (!(o instanceof Map.Entry<?, ?> e))1393return false;13941395return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&1396(value==null ? e.getValue()==null : value.equals(e.getValue()));1397}13981399public int hashCode() {1400return hash ^ Objects.hashCode(value);1401}14021403public String toString() {1404return key.toString()+"="+value.toString();1405}1406}14071408// Types of Enumerations/Iterations1409private static final int KEYS = 0;1410private static final int VALUES = 1;1411private static final int ENTRIES = 2;14121413/**1414* A hashtable enumerator class. This class implements both the1415* Enumeration and Iterator interfaces, but individual instances1416* can be created with the Iterator methods disabled. This is necessary1417* to avoid unintentionally increasing the capabilities granted a user1418* by passing an Enumeration.1419*/1420private class Enumerator<T> implements Enumeration<T>, Iterator<T> {1421final Entry<?,?>[] table = Hashtable.this.table;1422int index = table.length;1423Entry<?,?> entry;1424Entry<?,?> lastReturned;1425final int type;14261427/**1428* Indicates whether this Enumerator is serving as an Iterator1429* or an Enumeration. (true -> Iterator).1430*/1431final boolean iterator;14321433/**1434* The modCount value that the iterator believes that the backing1435* Hashtable should have. If this expectation is violated, the iterator1436* has detected concurrent modification.1437*/1438protected int expectedModCount = Hashtable.this.modCount;14391440Enumerator(int type, boolean iterator) {1441this.type = type;1442this.iterator = iterator;1443}14441445public boolean hasMoreElements() {1446Entry<?,?> e = entry;1447int i = index;1448Entry<?,?>[] t = table;1449/* Use locals for faster loop iteration */1450while (e == null && i > 0) {1451e = t[--i];1452}1453entry = e;1454index = i;1455return e != null;1456}14571458@SuppressWarnings("unchecked")1459public T nextElement() {1460Entry<?,?> et = entry;1461int i = index;1462Entry<?,?>[] t = table;1463/* Use locals for faster loop iteration */1464while (et == null && i > 0) {1465et = t[--i];1466}1467entry = et;1468index = i;1469if (et != null) {1470Entry<?,?> e = lastReturned = entry;1471entry = e.next;1472return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);1473}1474throw new NoSuchElementException("Hashtable Enumerator");1475}14761477// Iterator methods1478public boolean hasNext() {1479return hasMoreElements();1480}14811482public T next() {1483if (Hashtable.this.modCount != expectedModCount)1484throw new ConcurrentModificationException();1485return nextElement();1486}14871488public void remove() {1489if (!iterator)1490throw new UnsupportedOperationException();1491if (lastReturned == null)1492throw new IllegalStateException("Hashtable Enumerator");1493if (modCount != expectedModCount)1494throw new ConcurrentModificationException();14951496synchronized(Hashtable.this) {1497Entry<?,?>[] tab = Hashtable.this.table;1498int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;14991500@SuppressWarnings("unchecked")1501Entry<K,V> e = (Entry<K,V>)tab[index];1502for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {1503if (e == lastReturned) {1504if (prev == null)1505tab[index] = e.next;1506else1507prev.next = e.next;1508expectedModCount++;1509lastReturned = null;1510Hashtable.this.modCount++;1511Hashtable.this.count--;1512return;1513}1514}1515throw new ConcurrentModificationException();1516}1517}1518}1519}152015211522