The previous article has translated the Hashtable source code top annotation, this article will explain in detail Hashtable each method realization. Hashtable Hierarchy Chart
Let's look at the definition of Hashtable:
public class Hashtable<k,v> extends dictionary<k,v> implements Map<k,v>, Cloneable, Java.io.Serializable
From this we can learn that Hashtable<k,v>:hashmap is the extends Dictionary<k,v>:dictionary class that stores data in key-value form is an abstract class for storing keys/ value pairs, which act like the map class. Implements MAP<K,V>: Implements the map, implements the operation and the default method declared in the map. Implements Cloneable: Indicates that it can invoke the Clone () method to return the Field-for-field copy of the instance. Implements Java.io.Serializable: Indicates that the class can be serialized. Partial Global Variables
Here is a partial global variable, the global variable associated with the iterator and enumeration class, which is given next to the corresponding method or inner class class.
/**
* hashtable stored data.
*
private transient entry<?,? >[] table;
The
number of entry stored in the/** * Hashtable.
*
/private transient int count;
/**
* Hashtable the critical value for capacity expansion (this value is equal to (int) (capacity * loadfactor))
*
* @serial/
private int threshold;
/**
* load factor.
*
* @serial
/private float loadfactor;
/**
* The number of times the Hashtable was modified by the structure.
*
private transient int modcount = 0;
Hashtable does not require the capacity of the underlying array to be 2 of the integer power, while the HashMap requires a number of integer power of 2. Constructors
Hashtable has four constructors Hashtable (int initialcapacity, float loadfactor)
/** * Constructs an empty hashtable using the specified parameters to initialize the capacity and specify the parameter load factor. * * @param initialcapacity Specify parameter initialization capacity * @param loadfactor specified parameter load factor * @exception ILLEGALARGUMENTEXCEP
tion if the initialcapacity is less than 0 or the load factor is not positive.
*/public Hashtable (int initialcapacity, float loadfactor) {//If the specified parameter initialization capacity is less than 0, throw an exception if (Initialcapacity < 0)
throw new IllegalArgumentException ("illegal Capacity:" + initialcapacity); If the specified parameter load factor is not positive, throw an exception if (loadfactor <= 0 | |
Float.isnan (Loadfactor)) throw new IllegalArgumentException ("Illegal Load:" +loadfactor);
Initialize Hashtable loadfactor, table, Threshold properties if (initialcapacity==0) initialcapacity = 1;
This.loadfactor = Loadfactor;
Table = new entry<?,? >[initialCapacity]; If the initialcapacity * Loadfactor exceeds max_array_size, use max_array_size as threshold threshold = (int) math.min (
Initialcapacity * Loadfactor, max_array_size + 1); }
Hashtable (int initialcapacity)
/**
* Constructs an empty hashtable using the specified parameter initialization capacity and the default load factor (0.75).
*
* @param initialcapacity Specify parameter initialization capacity
* @exception illegalargumentexception if initialcapacity is less than 0
* * Public
Hashtable (int initialcapacity) {This
(initialcapacity, 0.75f);
}
Hashtable ()
/**
* Constructs an empty hashtable using the default initialization capacity (11) and the default load factor (0.75).
*
* Here you can see that the default initialization capacity of Hashtable is 16, while the default initialization capacity of HashMap is 11.
* * Public
Hashtable () {This
(0.75f);
}
Hashtable (map< extends K,? extends v> t)
/**
* Constructs hashtable using the specified key value pairs of set T.
*
* @param t-specified key-value pair collection
* @throws nullpointerexception If the specified key value pairs the collection as null
* @since 1.2
* * Public Hashtable (MAP< extends K,? extends V> t) {
//initial HashMap this
(Math.max (), one), 0.75f); 2*t.size />//inserts a key value pair from T into the Hashtable
Putall (t);
Common Methods
size ()
/** * Returns the number of keys in Hashtable * * @return returns the number of keys in Hashtable/public
synchronized int Size () {return
count;
}
IsEmpty ()
/**
* Determines whether there is a key value pair mapping in the hashtable.
*
* * @return Hashtable does not have a key value pair mapping, returns TRUE, otherwise returns false *
/public
synchronized Boolean isempty () {
return count = = 0;
keys ()
/**
* Returns an enumeration of key keys in Hashtable.
*
* @return Returns an enumeration of key keys in Hashtable.
* @see Enumeration
* @see #elements () *
@see #keySet ()
* @see Map
* * * Public synchronized enumeration<k> keys () {return
this.<k>getenumeration (keys);
}
elements ()
/**
* Returns an enumeration of value in Hashtable.
*
* @return to return an enumeration of value in Hashtable
* @see java.util.Enumeration
* @see #keys ()
* @see #values ()
* @see Map */public
synchronized enumeration<v> elements () {
Return this.<v>getenumeration (VALUES);
}
contains (Object value)
/**
* Returns whether the hashtable contains the specified parameter value.
* This method is more expensive than the ContainsKey method *
* @param value Specifies the parameter value
* @return hashtable contains the specified parameter value
* @exception NullPointerException value is null
*
/Public Synchronized Boolean contains (Object value) {
if ( Value = = null) {
throw new NullPointerException ();
}
entry<?,? > tab[] = table;
You need to traverse hashtable table for a relatively large price for
(int i = tab.length; i--> 0;) {for
(entry<?,? > E = tab[i], e!= null; e = e.next) {
if (e.value.equals (value)) {return
True;
}} return
false;
}
Containsvalue (Object value)
/**
* Returns whether the hashtable contains the specified parameter value.
* This method is more expensive than the ContainsKey method *
* @param value Specifies the parameter value
* @return hashtable contains the specified parameter value
* @exception NullPointerException value is null
* @since 1.2
*
/public boolean containsvalue (Object value) { Return
contains (value);
}
ContainsKey (Object key)
/**
* Tests If the specified object is a key to this hashtable.
* To determine if Hashtable contains the specified parameter key
* @param key to specify the parameter key
* @return hashtable contains the specified parameter key, returns TRUE, otherwise returns false
* @throws NullPointerException If key is null
* @see #contains (Object)
*/Public
synchronized Boolean ContainsKey (Object key) {
entry<?,? > tab[] = table;
int hash = Key.hashcode ();
Because there is no need to traverse the table from scratch, the cost is smaller than the containsvalue
int index = (hash & 0x7fffffff)% Tab.length;
For (entry<?,? > E = tab[index]; e!= null; e = e.next) {
if ((E.hash = hash) && e.key.equals (key)) { return
true;
}
return false;
}
Because you don't have to traverse the table from scratch, it costs less than containsvalue.
As you can see here, Hashtable and HashMap have different ways of confirming the index of a key in an array.
-Hashtable through index = (hash & 0x7fffffff)% tab.length to confirm
-HashMap through i = (n-1) & hash to confirm get (Object key)
/**
* Returns the value of the specified parameter key mapping, if there is no corresponding mapping, returns NULL
*
* @param key specifies
the parameter * @return returns the value of the specified parameter key mapping, if there is no corresponding mapping, Returns null
* @throws nullpointerexception If the specified parameter key is null
* @see #put (Object, Object
)
* * Suppresswarnings ("unchecked") public
synchronized V get (Object key) {
entry<?,? > tab[] = table;
int hash = Key.hashcode ();
int index = (hash & 0x7fffffff)% Tab.length;
For (entry<?,? > E = tab[index]; e!= null; e = e.next) {
if ((E.hash = hash) && e.key.equals (key)) { Return
(V) e.value;
}
}
return null;
}
constant Max_array_size
/**
* Maximum capacity allocated to arrays
* Why subtract 8?
* Because some VMS retain some headers in the array, attempting to allocate this maximum storage capacity may result in an array capacity greater than the limit of the VM, resulting in OutOfMemoryError.
*
private static final int max_array_size = integer.max_value-8;
Rehash ()
/** * Increases the capacity of the hashtable, in order to store and find its entry more efficiently. * When the number of key-value pairs exceeds the critical value (capacity*load factor) This method automatically invokes the length into the original twice times +1 */* * * @SuppressWarnings ("unchecked") protected void Rehash (
{//log old capacity int oldcapacity = Table.length;
Record the old bucket array entry<?,? >[] oldmap = table;
Overflow-conscious Code//new capacity for the old capacity of twice times +1 int newcapacity = (oldcapacity << 1) + 1;
If the new capacity is greater than the maximum capacity max_array_size if (Newcapacity-max_array_size > 0) {//If the old capacity is max_array_size, the capacity does not change, the execution of the interrupt method
if (oldcapacity = = max_array_size)//Keep running with max_array_size buckets return;
If the old capacity is not max_array_size, the new capacity becomes max_array_size newcapacity = max_array_size;
//Create new array, capacity entry<?,? >[] newmap = new entry<?,? >[newCapacity];
Structural modification times +1 modcount++;
Calculation of the critical value of expansion threshold = (int) math.min (newcapacity * loadfactor, max_array_size + 1);
Table = Newmap; Moves the key value pairs in the old array to the new array for (int i = oldcapacity ; i--> 0;) {for (entry<k,v> old = (entry<k,v>) oldmap[i]; old!= null;)
{entry<k,v> e = old;
Old = Old.next;
int index = (e.hash & 0x7fffffff)% Newcapacity;
E.next = (entry<k,v>) Newmap[index];
Newmap[index] = e; }
}
}
After reading the code, we can sum up the general idea of rehash is: New variable capacity, the value of the old capacity of twice times +1 if the new capacity is greater than the maximum capacity max_array_size
If the old capacity is max_array_size and the capacity is unchanged, the execution of the interrupt method if the old capacity is not max_array_size, the new capacity becomes max_array_size creates a new array, and the capacity for the new capacity moves the key value pairs in the old array to the new array
Here you can see, under normal circumstances, HashMap capacity into the original twice times, and Hashtable capacity after the expansion of the original twice Times +1. addentry (int hash, K key, V value, int index)
/**
* Add entry * to table by referring to parameters
This method is used by the
*
/private void addentry (int hash, K key, V value, int index) {
//Structural modification times +1
modcount++;
Record the current table
entry<?,? > tab[] = table;
If the current number of entry is greater than the critical value if
(Count >= threshold) {
//expansion
rehash ();
Record the new table
tab = table;
The hash hash of the key is recalculated
= Key.hashcode ();
Recalculate Index
index = (hash & 0x7fffffff)% tab.length
}
Create a new Entry
@SuppressWarnings ("unchecked")
entry<k,v> e = (entry<k,v>) tab[index];
Add Entry to table
Tab[index] = new entry<> (hash, key, value, e);
Table size +1
count++;
}
Put (K key, V value)
/** * Add the specified key value to the Hashtable * the key and value in the value pair added cannot be null * * value can be taken out of the Get method.
* * @param key The Hashtable key * @param value The value * @return if key already exists in Hashtable, return the original value * @exception NullPointerException if key or value is null * @see object#equals (object) * @see #get (object)/Pub Lic synchronized v put (K key, V value) {//Confirm value is not NULL if (value = = null) {throw new nullpointerexcept
Ion ();
} entry<?,? > tab[] = table;
int hash = Key.hashcode ();
Find the index of key in table int index = (hash & 0x7fffffff)% Tab.length;
@SuppressWarnings ("unchecked")//Get Entry entry<k,v> Entry = (entry<k,v>) Tab[index] with key index; Traversal entry to determine if key already exists for (; entry!= null; entry = Entry.next) {//If key already exists if (Entry.hash = hash)
&& entry.key.equals (Key)) {//save old value v. = Entry.value;
Replace value Entry.value = value; Returns the old value of return;
}//If key does not already exist in Hashtable, add the key value pair directly to the table, returning null addentry (hash, key, value, index);
return null; }
The general idea of Hashtable's put method can be summed up in code: Verify that value is not NULL. If NULL, then throw an exception to find the key in the table index, get the key location of the entry traversal entry, to determine whether the key already exists if the key already exists, replace value, return the old value If the key does not already exist in the Hashtable, it is added directly, otherwise the key value pair is added directly to the table, returning NULL
As you can see in the method, the elements in the bucket are traversed in the form of a linked list. It can be verified that the HashMap bucket may be a linked list or a tree. But the Hashtable bucket could only be a linked list. Remove (Object key)
/** * Deletes the key value pairs of the parameter key mappings in Hashtable.
If the parameter key does not exist in Hashtable, the method does nothing.
* * @param key Parameter key * @return The value of the parameter key mapping, or NULL if no corresponding mapping exists. * @throws NullPointerException If key is null/public synchronized V remove (Object key) {entry<?,? > tab[] = ta
ble
int hash = Key.hashcode ();
Calculates the index of the key in hashtable int index = (hash & 0x7fffffff)% Tab.length;
@SuppressWarnings ("unchecked") entry<k,v> E = (entry<k,v>) Tab[index]; Traversal Entry, if a key-value pair exists for the key in Entry, deletes the key-value pair and returns the value for the key-value pair (entry<k,v> prev = null; e!= null; prev = e, E = E.N
EXT) {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;
}//If there is no key-value pair with key as parameter key, returns value return null; }
From the code can be summed up the general idea of the Hashtable Remove method: Find the key in the table index, get key location of the entry traversal entry, to determine whether the key already exists if key exists, delete key mapping of the value pairs, Returns the old value if the key does not exist in Hashtable, returns a null Putall (map<? extends K,? extends v> t)
/**
* Copies all the key value pairs in the specified T to the Hashtable.
* If the key in T is already present in the Hashtable, replace it.
*
* @param t specifies the parameter T
* @throws nullpointerexception if the specified parameter T is null
* @since 1.2
/Public synchronized void Putall (map<? extends K,? extends V> t) {
//traversal of all key-value pairs in the parameter T, copy them to the Hashtable for
( map.entry<? Extends K,? Extends V> E:t.entryset ()) Put
(E.getkey (), E.getvalue ());
Clear ()
/**
* Empty hashtable all key value pairs/public
synchronized void Clear () {
entry<?,? > tab[] = table;
modcount++;
Iterates through all the entry in the Hashtable, placing it null, for
(int index = tab.length;--index >= 0;)
Tab[index] = null;
Modify Hashtable size to 0
count = 0;
}
Clone ()
/** * Creates a shallow copy of the Hashtable.
* Hashtable all structures are copied, but the key value pairs are not copied.
* This is a relatively expensive operation.
* * @return A hashtable light copy/Public synchronized Object clone () {try {//Call the parent class's Clone method, shallow copy a Hashtable object T
hashtable<?,? > t = (hashtable<?,? >) super.clone ();
Assign value to table property t.table = new entry<?,? >[table.length];
Iterate through the original hash array and copy and generate the list of each bucket individually. for (int i = table.length; i--> 0;) {T.table[i] = (Table[i]!= null)?
(entry<?,? >) table[i].clone (): null;
///Assign value to keyset property T.keyset = null;
Assign a value to the EntrySet property T.entryset = null;
Assign value to values attribute t.values = null;
Assign a value to the Modcount property T.modcount = 0;
Returns a shallow copy return t; catch (Clonenotsupportedexception e) {//This shouldn ' t happen, since we are cloneable throw new intern
Alerror (e); }
}
toString ()
/**
* Returns the string representation of the Hashtable.
*
* @return string representation of the Hashtable.
*/Public
synchronized String toString () {
int max = size ()-1;
If the hashtable size is 0, returns "{}"
if (max = = 1) return
"{}";
Use StringBuilder
StringBuilder sb = new StringBuilder ();
Gets the Entry iterator
iterator<map.entry<k,v>> it = EntrySet (). iterator ();
Sb.append (' {');
Traversal hashtable for
(int i = 0;; i++) {
map.entry<k,v> e = It.next ();
K key = E.getkey ();
V value = E.getvalue ();
Assembly hashtable string representation
sb.append (key = = this?) "(This Map)": key.tostring ());
Sb.append (' = ');
Sb.append (value = = this?) "(This Map)": value.tostring ());
I=max=size ()-1, to the last entry
if (i = = max) return
sb.append ('} '). toString ();
Sb.append (",");
}
}
getenumeration (int type)
/**
* Get Hashtable enum class object
*
* @param type 0--The enumeration class object/iterator that returns the key, 1--an enumeration class object/iterator that returns values, 2--an enumeration class object/iterator that returns entry
*
Private <T> enumeration<t> getenumeration (int type) {
if (count = = 0) {return
Collections.emptyenumeration ();
} else {
//false, meaning that it is not an iterator, and if true, that the iterator return
new enumerator<> (type, false) is returned;
The type of enumerations/iterations.
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;
getiterator (int type)
/**
* Gets hashtable iterator
*
* @param type 0--Returns an enumerated class object/iterator for the key, 1--an enumeration class object/iterator that returns values, 2--enum class object/iterator returning entry
*
Private <T> iterator<t> getiterator (int typ