Java框架,JDK7集合框架源碼閱讀(五) Hashtable

 2023-11-07 阅读 19 评论 0

摘要:基于版本jdk1.7.0_80 java.util.Hashtable ? 代碼如下 /** Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************/package java.util; import java.io.*

基于版本jdk1.7.0_80

java.util.Hashtable

?

代碼如下

/** Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************/package java.util;
import java.io.*;/*** This class implements a hash table, which maps keys to values. Any* non-<code>null</code> object can be used as a key or as a value. <p>** To successfully store and retrieve objects from a hashtable, the* objects used as keys must implement the <code>hashCode</code>* method and the <code>equals</code> method. <p>** An instance of <code>Hashtable</code> has two parameters that affect its* performance: <i>initial capacity</i> and <i>load factor</i>.  The* <i>capacity</i> is the number of <i>buckets</i> in the hash table, and the* <i>initial capacity</i> is simply the capacity at the time the hash table* is created.  Note that the hash table is <i>open</i>: in the case of a "hash* collision", a single bucket stores multiple entries, which must be searched* sequentially.  The <i>load factor</i> is a measure of how full the hash* table is allowed to get before its capacity is automatically increased.* The initial capacity and load factor parameters are merely hints to* the implementation.  The exact details as to when and whether the rehash* method is invoked are implementation-dependent.<p>** Generally, the default load factor (.75) offers a good tradeoff between* time and space costs.  Higher values decrease the space overhead but* increase the time cost to look up an entry (which is reflected in most* <tt>Hashtable</tt> operations, including <tt>get</tt> and <tt>put</tt>).<p>** The initial capacity controls a tradeoff between wasted space and the* need for <code>rehash</code> operations, which are time-consuming.* No <code>rehash</code> operations will <i>ever</i> occur if the initial* capacity is greater than the maximum number of entries the* <tt>Hashtable</tt> will contain divided by its load factor.  However,* setting the initial capacity too high can waste space.<p>** If many entries are to be made into a <code>Hashtable</code>,* creating it with a sufficiently large capacity may allow the* entries to be inserted more efficiently than letting it perform* automatic rehashing as needed to grow the table. <p>** This example creates a hashtable of numbers. It uses the names of* the numbers as keys:* <pre>   {@code*   Hashtable<String, Integer> numbers*     = new Hashtable<String, Integer>();*   numbers.put("one", 1);*   numbers.put("two", 2);*   numbers.put("three", 3);}</pre>** <p>To retrieve a number, use the following code:* <pre>   {@code*   Integer n = numbers.get("two");*   if (n != null) {*     System.out.println("two = " + n);*   }}</pre>** <p>The iterators returned by the <tt>iterator</tt> method of the collections* returned by all of this class's "collection view methods" are* <em>fail-fast</em>: if the Hashtable is structurally modified at any time* after the iterator is created, in any way except through the iterator's own* <tt>remove</tt> method, the iterator will throw a {@link* ConcurrentModificationException}.  Thus, in the face of concurrent* modification, the iterator fails quickly and cleanly, rather than risking* arbitrary, non-deterministic behavior at an undetermined time in the future.* The Enumerations returned by Hashtable's keys and elements methods are* <em>not</em> fail-fast.** <p>Note that the fail-fast behavior of an iterator cannot be guaranteed* as it is, generally speaking, impossible to make any hard guarantees in the* presence of unsynchronized concurrent modification.  Fail-fast iterators* throw <tt>ConcurrentModificationException</tt> on a best-effort basis.* Therefore, it would be wrong to write a program that depended on this* exception for its correctness: <i>the fail-fast behavior of iterators* should be used only to detect bugs.</i>** <p>As of the Java 2 platform v1.2, this class was retrofitted to* implement the {@link Map} interface, making it a member of the* <a href="{@docRoot}/../technotes/guides/collections/index.html">** Java Collections Framework</a>.  Unlike the new collection* implementations, {@code Hashtable} is synchronized.  If a* thread-safe implementation is not needed, it is recommended to use* {@link HashMap} in place of {@code Hashtable}.  If a thread-safe* highly-concurrent implementation is desired, then it is recommended* to use {@link java.util.concurrent.ConcurrentHashMap} in place of* {@code Hashtable}.** @author  Arthur van Hoff* @author  Josh Bloch* @author  Neal Gafter* @see     Object#equals(java.lang.Object)* @see     Object#hashCode()* @see     Hashtable#rehash()* @see     Collection* @see     Map* @see     HashMap* @see     TreeMap* @since JDK1.0*/
public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable {/*** The hash table data.*/private transient Entry<K,V>[] table;/*** The total number of entries in the hash table.*/private transient int count;/*** The table is rehashed when its size exceeds this threshold.  (The* value of this field is (int)(capacity * loadFactor).)** @serial*/private int threshold;/*** The load factor for the hashtable.** @serial*/private float loadFactor;/*** The number of times this Hashtable has been structurally modified* Structural modifications are those that change the number of entries in* the Hashtable or otherwise modify its internal structure (e.g.,* rehash).  This field is used to make iterators on Collection-views of* the Hashtable fail-fast.  (See ConcurrentModificationException).*/private transient int modCount = 0;/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = 1421746759512286392L;/*** The default threshold of map capacity above which alternative hashing is* used for String keys. Alternative hashing reduces the incidence of* collisions due to weak hash code calculation for String keys.* <p>* This value may be overridden by defining the system property* {@code jdk.map.althashing.threshold}. A property value of {@code 1}* forces alternative hashing to be used at all times whereas* {@code -1} value ensures that alternative hashing is never used.*/static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;/*** holds values which can't be initialized until after VM is booted.*/private static class Holder {/*** Table capacity above which to switch to use alternative hashing.*/static final int ALTERNATIVE_HASHING_THRESHOLD;static {String altThreshold = java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("jdk.map.althashing.threshold"));int threshold;try {threshold = (null != altThreshold)? Integer.parseInt(altThreshold): ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;// disable alternative hashing if -1if (threshold == -1) {threshold = Integer.MAX_VALUE;}if (threshold < 0) {throw new IllegalArgumentException("value must be positive integer.");}} catch(IllegalArgumentException failed) {throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);}ALTERNATIVE_HASHING_THRESHOLD = threshold;}}/*** A randomizing value associated with this instance that is applied to* hash code of keys to make hash collisions harder to find.*/transient int hashSeed;/*** Initialize the hashing mask value.*/final boolean initHashSeedAsNeeded(int capacity) {boolean currentAltHashing = hashSeed != 0;boolean useAltHashing = sun.misc.VM.isBooted() &&(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);boolean switching = currentAltHashing ^ useAltHashing;if (switching) {hashSeed = useAltHashing? sun.misc.Hashing.randomHashSeed(this): 0;}return switching;}private int hash(Object k) {// hashSeed will be zero if alternative hashing is disabled.return hashSeed ^ k.hashCode();}/*** Constructs a new, empty hashtable with the specified initial* capacity and the specified load factor.** @param      initialCapacity   the initial capacity of the hashtable.* @param      loadFactor        the load factor of the hashtable.* @exception  IllegalArgumentException  if the initial capacity is less*             than zero, or if the load factor is nonpositive.*/public Hashtable(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal Load: "+loadFactor);if (initialCapacity==0)initialCapacity = 1;this.loadFactor = loadFactor;table = new Entry[initialCapacity];threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);initHashSeedAsNeeded(initialCapacity);}/*** Constructs a new, empty hashtable with the specified initial capacity* and default load factor (0.75).** @param     initialCapacity   the initial capacity of the hashtable.* @exception IllegalArgumentException if the initial capacity is less*              than zero.*/public Hashtable(int initialCapacity) {this(initialCapacity, 0.75f);}/*** Constructs a new, empty hashtable with a default initial capacity (11)* and load factor (0.75).*/public Hashtable() {this(11, 0.75f);}/*** Constructs a new hashtable with the same mappings as the given* Map.  The hashtable is created with an initial capacity sufficient to* hold the mappings in the given Map and a default load factor (0.75).** @param t the map whose mappings are to be placed in this map.* @throws NullPointerException if the specified map is null.* @since   1.2*/public Hashtable(Map<? extends K, ? extends V> t) {this(Math.max(2*t.size(), 11), 0.75f);putAll(t);}/*** Returns the number of keys in this hashtable.** @return  the number of keys in this hashtable.*/public synchronized int size() {return count;}/*** Tests if this hashtable maps no keys to values.** @return  <code>true</code> if this hashtable maps no keys to values;*          <code>false</code> otherwise.*/public synchronized boolean isEmpty() {return count == 0;}/*** Returns an enumeration of the keys in this hashtable.** @return  an enumeration of the keys in this hashtable.* @see     Enumeration* @see     #elements()* @see     #keySet()* @see     Map*/public synchronized Enumeration<K> keys() {return this.<K>getEnumeration(KEYS);}/*** Returns an enumeration of the values in this hashtable.* Use the Enumeration methods on the returned object to fetch the elements* sequentially.** @return  an enumeration of the values in this hashtable.* @see     java.util.Enumeration* @see     #keys()* @see     #values()* @see     Map*/public synchronized Enumeration<V> elements() {return this.<V>getEnumeration(VALUES);}/*** Tests if some key maps into the specified value in this hashtable.* This operation is more expensive than the {@link #containsKey* containsKey} method.** <p>Note that this method is identical in functionality to* {@link #containsValue containsValue}, (which is part of the* {@link Map} interface in the collections framework).** @param      value   a value to search for* @return     <code>true</code> if and only if some key maps to the*             <code>value</code> argument in this hashtable as*             determined by the <tt>equals</tt> method;*             <code>false</code> otherwise.* @exception  NullPointerException  if the value is <code>null</code>*/public synchronized boolean contains(Object value) {if (value == null) {throw new NullPointerException();}Entry tab[] = table;for (int i = tab.length ; i-- > 0 ;) {for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {if (e.value.equals(value)) {return true;}}}return false;}/*** Returns true if this hashtable maps one or more keys to this value.** <p>Note that this method is identical in functionality to {@link* #contains contains} (which predates the {@link Map} interface).** @param value value whose presence in this hashtable is to be tested* @return <tt>true</tt> if this map maps one or more keys to the*         specified value* @throws NullPointerException  if the value is <code>null</code>* @since 1.2*/public boolean containsValue(Object value) {return contains(value);}/*** Tests if the specified object is a key in this hashtable.** @param   key   possible key* @return  <code>true</code> if and only if the specified object*          is a key in this hashtable, as determined by the*          <tt>equals</tt> method; <code>false</code> otherwise.* @throws  NullPointerException  if the key is <code>null</code>* @see     #contains(Object)*/public synchronized boolean containsKey(Object key) {Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {return true;}}return false;}/*** Returns the value to which the specified key is mapped,* or {@code null} if this map contains no mapping for the key.** <p>More formally, if this map contains a mapping from a key* {@code k} to a value {@code v} such that {@code (key.equals(k))},* then this method returns {@code v}; otherwise it returns* {@code null}.  (There can be at most one such mapping.)** @param key the key whose associated value is to be returned* @return the value to which the specified key is mapped, or*         {@code null} if this map contains no mapping for the key* @throws NullPointerException if the specified key is null* @see     #put(Object, Object)*/public synchronized V get(Object key) {Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {return e.value;}}return null;}/*** The maximum size of array to allocate.* Some VMs reserve some header words in an array.* Attempts to allocate larger arrays may result in* OutOfMemoryError: Requested array size exceeds VM limit*/private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/*** Increases the capacity of and internally reorganizes this* hashtable, in order to accommodate and access its entries more* efficiently.  This method is called automatically when the* number of keys in the hashtable exceeds this hashtable's capacity* and load factor.*/protected void rehash() {int oldCapacity = table.length;Entry<K,V>[] oldMap = table;// overflow-conscious codeint newCapacity = (oldCapacity << 1) + 1;if (newCapacity - MAX_ARRAY_SIZE > 0) {if (oldCapacity == MAX_ARRAY_SIZE)// Keep running with MAX_ARRAY_SIZE bucketsreturn;newCapacity = MAX_ARRAY_SIZE;}Entry<K,V>[] newMap = new Entry[newCapacity];modCount++;threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);boolean rehash = initHashSeedAsNeeded(newCapacity);table = newMap;for (int i = oldCapacity ; i-- > 0 ;) {for (Entry<K,V> old = oldMap[i] ; old != null ; ) {Entry<K,V> e = old;old = old.next;if (rehash) {e.hash = hash(e.key);}int index = (e.hash & 0x7FFFFFFF) % newCapacity;e.next = newMap[index];newMap[index] = e;}}}/*** Maps the specified <code>key</code> to the specified* <code>value</code> in this hashtable. Neither the key nor the* value can be <code>null</code>. <p>** The value can be retrieved by calling the <code>get</code> method* with a key that is equal to the original key.** @param      key     the hashtable key* @param      value   the value* @return     the previous value of the specified key in this hashtable,*             or <code>null</code> if it did not have one* @exception  NullPointerException  if the key or value is*               <code>null</code>* @see     Object#equals(Object)* @see     #get(Object)*/public synchronized V put(K key, V value) {// Make sure the value is not nullif (value == null) {throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {V old = e.value;e.value = value;return old;}}modCount++;if (count >= threshold) {// Rehash the table if the threshold is exceeded
            rehash();tab = table;hash = hash(key);index = (hash & 0x7FFFFFFF) % tab.length;}// Creates the new entry.Entry<K,V> e = tab[index];tab[index] = new Entry<>(hash, key, value, e);count++;return null;}/*** Removes the key (and its corresponding value) from this* hashtable. This method does nothing if the key is not in the hashtable.** @param   key   the key that needs to be removed* @return  the value to which the key had been mapped in this hashtable,*          or <code>null</code> if the key did not have a mapping* @throws  NullPointerException  if the key is <code>null</code>*/public synchronized V remove(Object key) {Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {modCount++;if (prev != null) {prev.next = e.next;} else {tab[index] = e.next;}count--;V oldValue = e.value;e.value = null;return oldValue;}}return null;}/*** Copies all of the mappings from the specified map to this hashtable.* These mappings will replace any mappings that this hashtable had for any* of the keys currently in the specified map.** @param t mappings to be stored in this map* @throws NullPointerException if the specified map is null* @since 1.2*/public synchronized void putAll(Map<? extends K, ? extends V> t) {for (Map.Entry<? extends K, ? extends V> e : t.entrySet())put(e.getKey(), e.getValue());}/*** Clears this hashtable so that it contains no keys.*/public synchronized void clear() {Entry tab[] = table;modCount++;for (int index = tab.length; --index >= 0; )tab[index] = null;count = 0;}/*** Creates a shallow copy of this hashtable. All the structure of the* hashtable itself is copied, but the keys and values are not cloned.* This is a relatively expensive operation.** @return  a clone of the hashtable*/public synchronized Object clone() {try {Hashtable<K,V> t = (Hashtable<K,V>) super.clone();t.table = new Entry[table.length];for (int i = table.length ; i-- > 0 ; ) {t.table[i] = (table[i] != null)? (Entry<K,V>) table[i].clone() : null;}t.keySet = null;t.entrySet = null;t.values = null;t.modCount = 0;return t;} catch (CloneNotSupportedException e) {// this shouldn't happen, since we are Cloneablethrow new InternalError();}}/*** Returns a string representation of this <tt>Hashtable</tt> object* in the form of a set of entries, enclosed in braces and separated* by the ASCII characters "<tt>,&nbsp;</tt>" (comma and space). Each* entry is rendered as the key, an equals sign <tt>=</tt>, and the* associated element, where the <tt>toString</tt> method is used to* convert the key and element to strings.** @return  a string representation of this hashtable*/public synchronized String toString() {int max = size() - 1;if (max == -1)return "{}";StringBuilder sb = new StringBuilder();Iterator<Map.Entry<K,V>> it = entrySet().iterator();sb.append('{');for (int i = 0; ; i++) {Map.Entry<K,V> e = it.next();K key = e.getKey();V value = e.getValue();sb.append(key   == this ? "(this Map)" : key.toString());sb.append('=');sb.append(value == this ? "(this Map)" : value.toString());if (i == max)return sb.append('}').toString();sb.append(", ");}}private <T> Enumeration<T> getEnumeration(int type) {if (count == 0) {return Collections.emptyEnumeration();} else {return new Enumerator<>(type, false);}}private <T> Iterator<T> getIterator(int type) {if (count == 0) {return Collections.emptyIterator();} else {return new Enumerator<>(type, true);}}// Views/*** Each of these fields are initialized to contain an instance of the* appropriate view the first time this view is requested.  The views are* stateless, so there's no reason to create more than one of each.*/private transient volatile Set<K> keySet = null;private transient volatile Set<Map.Entry<K,V>> entrySet = null;private transient volatile Collection<V> values = null;/*** Returns a {@link Set} view of the keys contained in this map.* The set is backed by the map, so changes to the map are* reflected in the set, and vice-versa.  If the map is modified* while an iteration over the set is in progress (except through* the iterator's own <tt>remove</tt> operation), the results of* the iteration are undefined.  The set supports element removal,* which removes the corresponding mapping from the map, via the* <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,* <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>* operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>* operations.** @since 1.2*/public Set<K> keySet() {if (keySet == null)keySet = Collections.synchronizedSet(new KeySet(), this);return keySet;}private class KeySet extends AbstractSet<K> {public Iterator<K> iterator() {return getIterator(KEYS);}public int size() {return count;}public boolean contains(Object o) {return containsKey(o);}public boolean remove(Object o) {return Hashtable.this.remove(o) != null;}public void clear() {Hashtable.this.clear();}}/*** Returns a {@link Set} view of the mappings contained in this map.* The set is backed by the map, so changes to the map are* reflected in the set, and vice-versa.  If the map is modified* while an iteration over the set is in progress (except through* the iterator's own <tt>remove</tt> operation, or through the* <tt>setValue</tt> operation on a map entry returned by the* iterator) the results of the iteration are undefined.  The set* supports element removal, which removes the corresponding* mapping from the map, via the <tt>Iterator.remove</tt>,* <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and* <tt>clear</tt> operations.  It does not support the* <tt>add</tt> or <tt>addAll</tt> operations.** @since 1.2*/public Set<Map.Entry<K,V>> entrySet() {if (entrySet==null)entrySet = Collections.synchronizedSet(new EntrySet(), this);return entrySet;}private class EntrySet extends AbstractSet<Map.Entry<K,V>> {public Iterator<Map.Entry<K,V>> iterator() {return getIterator(ENTRIES);}public boolean add(Map.Entry<K,V> o) {return super.add(o);}public boolean contains(Object o) {if (!(o instanceof Map.Entry))return false;Map.Entry entry = (Map.Entry)o;Object key = entry.getKey();Entry[] tab = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry e = tab[index]; e != null; e = e.next)if (e.hash==hash && e.equals(entry))return true;return false;}public boolean remove(Object o) {if (!(o instanceof Map.Entry))return false;Map.Entry<K,V> entry = (Map.Entry<K,V>) o;K key = entry.getKey();Entry[] tab = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index], prev = null; e != null;prev = e, e = e.next) {if (e.hash==hash && e.equals(entry)) {modCount++;if (prev != null)prev.next = e.next;elsetab[index] = e.next;count--;e.value = null;return true;}}return false;}public int size() {return count;}public void clear() {Hashtable.this.clear();}}/*** Returns a {@link Collection} view of the values contained in this map.* The collection is backed by the map, so changes to the map are* reflected in the collection, and vice-versa.  If the map is* modified while an iteration over the collection is in progress* (except through the iterator's own <tt>remove</tt> operation),* the results of the iteration are undefined.  The collection* supports element removal, which removes the corresponding* mapping from the map, via the <tt>Iterator.remove</tt>,* <tt>Collection.remove</tt>, <tt>removeAll</tt>,* <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not* support the <tt>add</tt> or <tt>addAll</tt> operations.** @since 1.2*/public Collection<V> values() {if (values==null)values = Collections.synchronizedCollection(new ValueCollection(),this);return values;}private class ValueCollection extends AbstractCollection<V> {public Iterator<V> iterator() {return getIterator(VALUES);}public int size() {return count;}public boolean contains(Object o) {return containsValue(o);}public void clear() {Hashtable.this.clear();}}// Comparison and hashing/*** Compares the specified Object with this Map for equality,* as per the definition in the Map interface.** @param  o object to be compared for equality with this hashtable* @return true if the specified Object is equal to this Map* @see Map#equals(Object)* @since 1.2*/public synchronized boolean equals(Object o) {if (o == this)return true;if (!(o instanceof Map))return false;Map<K,V> t = (Map<K,V>) o;if (t.size() != size())return false;try {Iterator<Map.Entry<K,V>> i = entrySet().iterator();while (i.hasNext()) {Map.Entry<K,V> e = i.next();K key = e.getKey();V value = e.getValue();if (value == null) {if (!(t.get(key)==null && t.containsKey(key)))return false;} else {if (!value.equals(t.get(key)))return false;}}} catch (ClassCastException unused)   {return false;} catch (NullPointerException unused) {return false;}return true;}/*** Returns the hash code value for this Map as per the definition in the* Map interface.** @see Map#hashCode()* @since 1.2*/public synchronized int hashCode() {/** This code detects the recursion caused by computing the hash code* of a self-referential hash table and prevents the stack overflow* that would otherwise result.  This allows certain 1.1-era* applets with self-referential hash tables to work.  This code* abuses the loadFactor field to do double-duty as a hashCode* in progress flag, so as not to worsen the space performance.* A negative load factor indicates that hash code computation is* in progress.*/int h = 0;if (count == 0 || loadFactor < 0)return h;  // Returns zero
loadFactor = -loadFactor;  // Mark hashCode computation in progressEntry[] tab = table;for (Entry<K,V> entry : tab)while (entry != null) {h += entry.hashCode();entry = entry.next;}loadFactor = -loadFactor;  // Mark hashCode computation completereturn h;}/*** Save the state of the Hashtable to a stream (i.e., serialize it).** @serialData The <i>capacity</i> of the Hashtable (the length of the*             bucket array) is emitted (int), followed by the*             <i>size</i> of the Hashtable (the number of key-value*             mappings), followed by the key (Object) and value (Object)*             for each key-value mapping represented by the Hashtable*             The key-value mappings are emitted in no particular order.*/private void writeObject(java.io.ObjectOutputStream s)throws IOException {Entry<K, V> entryStack = null;synchronized (this) {// Write out the length, threshold, loadfactor
            s.defaultWriteObject();// Write out length, count of elements
            s.writeInt(table.length);s.writeInt(count);// Stack copies of the entries in the tablefor (int index = 0; index < table.length; index++) {Entry<K,V> entry = table[index];while (entry != null) {entryStack =new Entry<>(0, entry.key, entry.value, entryStack);entry = entry.next;}}}// Write out the key/value objects from the stacked entrieswhile (entryStack != null) {s.writeObject(entryStack.key);s.writeObject(entryStack.value);entryStack = entryStack.next;}}/*** Reconstitute the Hashtable from a stream (i.e., deserialize it).*/private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException{// Read in the length, threshold, and loadfactor
        s.defaultReadObject();// Read the original length of the array and number of elementsint origlength = s.readInt();int elements = s.readInt();// Compute new size with a bit of room 5% to grow but// no larger than the original size.  Make the length// odd if it's large enough, this helps distribute the entries.// Guard against the length ending up zero, that's not valid.int length = (int)(elements * loadFactor) + (elements / 20) + 3;if (length > elements && (length & 1) == 0)length--;if (origlength > 0 && length > origlength)length = origlength;Entry<K,V>[] newTable = new Entry[length];threshold = (int) Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);count = 0;initHashSeedAsNeeded(length);// Read the number of elements and then all the key/value objectsfor (; elements > 0; elements--) {K key = (K)s.readObject();V value = (V)s.readObject();// synch could be eliminated for performance
            reconstitutionPut(newTable, key, value);}this.table = newTable;}/*** The put method used by readObject. This is provided because put* is overridable and should not be called in readObject since the* subclass will not yet be initialized.** <p>This differs from the regular put method in several ways. No* checking for rehashing is necessary since the number of elements* initially in the table is known. The modCount is not incremented* because we are creating a new instance. Also, no return value* is needed.*/private void reconstitutionPut(Entry<K,V>[] tab, K key, V value)throws StreamCorruptedException{if (value == null) {throw new java.io.StreamCorruptedException();}// Makes sure the key is not already in the hashtable.// This should not happen in deserialized version.int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {throw new java.io.StreamCorruptedException();}}// Creates the new entry.Entry<K,V> e = tab[index];tab[index] = new Entry<>(hash, key, value, e);count++;}/*** Hashtable bucket collision list entry*/private static class Entry<K,V> implements Map.Entry<K,V> {int hash;final K key;V value;Entry<K,V> next;protected Entry(int hash, K key, V value, Entry<K,V> next) {this.hash = hash;this.key =  key;this.value = value;this.next = next;}protected Object clone() {return new Entry<>(hash, key, value,(next==null ? null : (Entry<K,V>) next.clone()));}// Map.Entry Opspublic K getKey() {return key;}public V getValue() {return value;}public V setValue(V value) {if (value == null)throw new NullPointerException();V oldValue = this.value;this.value = value;return oldValue;}public boolean equals(Object o) {if (!(o instanceof Map.Entry))return false;Map.Entry<?,?> e = (Map.Entry)o;return key.equals(e.getKey()) && value.equals(e.getValue());}public int hashCode() {return (Objects.hashCode(key) ^ Objects.hashCode(value));}public String toString() {return key.toString()+"="+value.toString();}}// Types of Enumerations/Iterationsprivate static final int KEYS = 0;private static final int VALUES = 1;private static final int ENTRIES = 2;/*** A hashtable enumerator class.  This class implements both the* Enumeration and Iterator interfaces, but individual instances* can be created with the Iterator methods disabled.  This is necessary* to avoid unintentionally increasing the capabilities granted a user* by passing an Enumeration.*/private class Enumerator<T> implements Enumeration<T>, Iterator<T> {Entry[] table = Hashtable.this.table;int index = table.length;Entry<K,V> entry = null;Entry<K,V> lastReturned = null;int type;/*** Indicates whether this Enumerator is serving as an Iterator* or an Enumeration.  (true -> Iterator).*/boolean iterator;/*** The modCount value that the iterator believes that the backing* Hashtable should have.  If this expectation is violated, the iterator* has detected concurrent modification.*/protected int expectedModCount = modCount;Enumerator(int type, boolean iterator) {this.type = type;this.iterator = iterator;}public boolean hasMoreElements() {Entry<K,V> e = entry;int i = index;Entry[] t = table;/* Use locals for faster loop iteration */while (e == null && i > 0) {e = t[--i];}entry = e;index = i;return e != null;}public T nextElement() {Entry<K,V> et = entry;int i = index;Entry[] t = table;/* Use locals for faster loop iteration */while (et == null && i > 0) {et = t[--i];}entry = et;index = i;if (et != null) {Entry<K,V> e = lastReturned = entry;entry = e.next;return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);}throw new NoSuchElementException("Hashtable Enumerator");}// Iterator methodspublic boolean hasNext() {return hasMoreElements();}public T next() {if (modCount != expectedModCount)throw new ConcurrentModificationException();return nextElement();}public void remove() {if (!iterator)throw new UnsupportedOperationException();if (lastReturned == null)throw new IllegalStateException("Hashtable Enumerator");if (modCount != expectedModCount)throw new ConcurrentModificationException();synchronized(Hashtable.this) {Entry[] tab = Hashtable.this.table;int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index], prev = null; e != null;prev = e, e = e.next) {if (e == lastReturned) {modCount++;expectedModCount++;if (prev == null)tab[index] = e.next;elseprev.next = e.next;count--;lastReturned = null;return;}}throw new ConcurrentModificationException();}}}
}
View Code

?

之前面試的時候,HashMap與Hashtable的區別基本是必問的,現在正好趁閱讀源碼的機會過一下

?

1. 接口分析

Hashtable繼承于Dictionary抽象類(與Map接口非常類似,官方文檔里已經將其標記為obsolete,并建議使用Map接口作為代替)

Cloneable,java.io.Serializable接口

?

2. 實現原理

與HashMap基本一致,用鏈表數組來存儲鍵值對,使用鏈地址法處理沖突

?

3. 擴容

newCapacity = (oldCapacity << 1) + 1;

?

4. 線程安全

所有的public方法都被加上了synchronized關鍵字,這樣就不會出現多線程下的異常問題了

但是在高并發的場景下,性能較低

?

5. 不支持key為null的情況

put/get方法都沒有對key為null的情況做額外處理,因此都會拋出異常

?

6. 迭代器與ConcurrentModificationException

Hashtable的迭代器也是快速失敗的,迭代器在建立之后,如果原Hashtable發生了變動,那么調用迭代器的next等方法就會拋出ConcurrentModificationException

?

?

?

那么總結一下HashMap與Hashtable的區別

1. HashMap繼承于Map接口與AbstractMap抽象類,Hashtable繼承于一個即將被廢棄的Dictionary抽象類

2. HashMap支持key為null的鍵值對,Hashtable不支持

3. 最重要的一點:HashMap不是線程安全,而Hashtable是線程安全的。(但是Hashtable的實現方式過于粗糙,最好還是使用ConcurrentHashMap為好)

4. HashMap有一個LinkedHashMap的子類,通過這個子類可以非常容易的實現可預期的迭代器操作(跟插入次序保持一致),Hashtable想做到這一點比較困難

?

轉載于:https://www.cnblogs.com/stevenczp/p/7131408.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/167061.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息