"Java Source code Analysis" Hashtable source analysis

Source: Internet
Author: User
Tags rehash

Definition of Class

public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable {}

Note that although Hashtable and HashMap are very similar, the parent classes of these two classes are not the same. The former is a subclass of the dictionary class, which is the subclass of the abstract map.

    1. HashTable is also a collection class that maps key to value,key is not allowed to be null, and a class that is a key must implement the Hashcode () and Equals () methods
    2. Two factors that affect the performance of Hashtable are also capacity capacity and load factor load-factor. This is the same as HashMap, the default loading factor is also 0.75
    3. The initialization capacity is used to control the balance between space utilization and expansion, and if the initial capacity is small, subsequent expansions can result in multiple expansion (the expansion will reallocate space, hash, and copy the data, so it is time consuming); If the initial capacity is large, storage space may be a bit wasteful; So, like HashMap, It is best to estimate a reasonable initial size
    4. While iterators are created and iterated, the same is not allowed to modify the hashtable structure (such as adding or deleting data), otherwise fail-fast is present, while Fail-fast is thrown ConcurrentModificationException but it does not guarantee thread safety
    5. Hashtable is thread-safe If the program itself is not running in a multithreaded environment, then it is recommended to use HashMap, if it is a high concurrency environment, it is recommended java.util.concurrent.ConcurrentHashMap to use only in general concurrency environment recommended Hashtable

Important Member variables

private transient Entry<K,V>[] table; // 基于数组实现 private transient int count; // 实际存放的实体个数private int threshold; // 阈值,用于判断是否需要扩容private float loadFactor; // 装载因子

constructor function

  Public Hashtable () {This (one, 0.75f);} Public Hashtable (Int. initialcapacity) {This (initialcapacity, 0.75f);} Public Hashtable (int initialcapacity, float loadfactor) {if (Initialcapacity < 0) throw new Illegalargumente    Xception ("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); usealthashing = sun.misc.VM.isBooted () && (initialcapacity >= holder.alternative_hashing_threshold);    }public Hashtable (map<? extends K,? extends V> t) {This (Math.max (2*t.size (), one), 0.75f); Putall (t);}  

Constructors and HashMap, the difference is that the default capacity is not the same,the default capacity of Hashtable is one

hash function

transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);private int hash(Object k) {    if (useAltHashing) {        if (k.getClass() == String.class) {            return sun.misc.Hashing.stringHash32((String) k);        } else {            int h = hashSeed ^ k.hashCode();            // This function ensures that hashCodes that differ only by            // constant multiples at each bit position have a bounded            // number of collisions (approximately 8 at default load factor).            h ^= (h >>> 20) ^ (h >>> 12);            return h ^ (h >>> 7) ^ (h >>> 4);         }    } else  {        return k.hashCode();    }}

The hash calculation is the same as the HashMap.

Determine if a given value exists

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;}public boolean containsValue(Object value) {    return contains(value);}

You can see here that if value is NULL, the exception is thrown directly, which directly indicates that Hashtable is not allowed to null-value. In addition, although the method is contains (), but the actual function and Containsvalue () is the same, from the implementation of Containsvalue () can be seen

Whether a given key exists

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 true if and only if the given key is present, but it is important to note that the presence here is dependent on how the Equals () method is implemented. This and the key that is emphasized in the beginning must implement the hashcode and the Equals method to be consistent. Here (hash & 0x7FFFFFFF) The purpose should be to ensure that the hash is a positive value, after all, only the 32nd bit is 0 others are 1

Use the given key to find

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;}

With HashMap, because the underlying implementation structure here is the same, all based on arrays, each subscript of an array corresponds to a single linked list. Returns the found value if the corresponding key exists in the Hashtable value, otherwise returns null

Expansion

protected void Rehash () {int oldcapacity = table.length;    entry<k,v>[] Oldmap = table;    overflow-conscious code int newcapacity = (oldcapacity << 1) + 1; if (Newcapacity-max_array_size > 0) {if (oldcapacity = = max_array_size)//Keep running with Max_a        Rray_size buckets return;    newcapacity = max_array_size;    } entry<k,v>[] Newmap = new Entry[newcapacity];    modcount++;    threshold = (int) math.min (newcapacity * loadfactor, max_array_size + 1);    Boolean currentalthashing = usealthashing;    usealthashing = sun.misc.VM.isBooted () && (newcapacity >= holder.alternative_hashing_threshold);    Boolean rehash = currentalthashing ^ usealthashing;    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; }    }}

When the number of key-value pairs that are actually loaded exceeds the length of the table, which is the product of the capacity capacity and the loading factor, the expansion is done automatically, but it is time consuming. after the expansion of capacity is twice times the original capacity plus 1, here and hashmap contrast is easy to understand, hashmap initial capacity is even, and requires capacity has been even, then the expansion of the time directly to the original twice times. Accordingly, the initial capacity of the Hashtable is an odd 11, twice times later becomes even, so add 1. However, there is no requirement that the capacity of the hashtable must be odd, but the capacity will become odd after expansion.

To add a key-value pair

  Public Synchronized v put (K key, V value) {//Make sure the value was not NULL if (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;}  

Note the keys and values must not be added during the add process if the given key already exists, replace the old value and return the old value, otherwise add the new key-value pair to Hashtable, and if the actual key-value pair is found to have exceeded the threshold value during the add process, then the expansion

Delete a key value pair

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;}

If a key value pair is found for a given key, then the delete operation is done and nothing is done if it is not found. The actual deletion process is the node deletion of the single linked list

Clear operation

public synchronized void clear() {    Entry tab[] = table;    modCount++;    for (int index = tab.length; --index >= 0; )        tab[index] = null;    count = 0;}

As with the HashMap, just set each slot to NULL, leaving the GC

Compare two hashtable for equality

  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;}  

First compare the size is equal, if the size is equal, then compare each entity in the entity collection is the same key value pair, if all the key value pairs are the same, then return true, otherwise false, note that because the data store in Hashtable is not ordered, So the actual comparison is using get () to get the corresponding value according to the specified key

Get Hashtable's Hashcode

  Public synchronized int hashcode () {/* * This code detects the recursion caused by computing the hash C  Ode * 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 does double-duty as a hashcode * in progress flags, so as not to worsen th     E space performance.     * A Negative load factor indicates that hash code computation are * in progress.    */int h = 0;  if (count = = 0 | | Loadfactor < 0) return h;  Returns zero loadfactor =-loadfactor;    Mark hashcode computation in progress entry[] tab = table;            for (entry<k,v> Entry:tab) while (Entry! = null) {H + = Entry.hashcode ();        Entry = Entry.next;  } loadfactor =-loadfactor; Mark hashcode computation complete return h;}  

The actual calculation of the Hashtable Hashcode is based on the hashcode of each entity, and the sum of Hashcode () of all entities is Hashtable hashcode. Loadfractor plays the role of a flag bit, which marks the beginning and end of the calculation. About the H + + Entry.hashcode () in the H will overflow problem, above the hash () function has seen each time the hash will be with the 0x7fffffff, that is, the hash is always positive, will not overflow

Serialization and deserialization

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 table for (int index = 0; index < table.length; index++) {Entr            Y<k,v> entry = Table[index]; while (entry! = null) {Entrystack = new entry<> (0, Entry.key, Entry.value, Entrys                Tack);            Entry = Entry.next; }}}//Write out the Key/value objects from the stacked entries while (Entrystack! = null) {S.WR        Iteobject (Entrystack.key);        S.writeobject (Entrystack.value);    Entrystack = Entrystack.next; }}private void ReadObject (Java.io.ObjectInputStream s) throws IOException, Classnotfoundexception{//Read in the length, threshold, and Loadfactor s.defaultreadobject ();    Set Hashseed Unsafe.putintvolatile (this, Hashseed_offset, Sun.misc.Hashing.randomHashSeed (this));    Read the original length of the array and number of elements int origlength = S.readint ();    int elements = S.readint ();  Compute new size with a bit of the class 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>[] table = new Entry[length];    threshold = (int) math.min (length * loadfactor, max_array_size + 1);    Count = 0; usealthashing = Sun.misc.VM.isBootEd () && (length >= holder.alternative_hashing_threshold); Read the number of elements and then all the Key/value objects for (; elements > 0; elements--) {K key =        (K) S.readobject ();        V value = (v) s.readobject ();    Synch could is eliminated for performance reconstitutionput (table, key, value); } this.table = table;}

Note that during deserialization, it does not recreate a hashtable that is exactly the same as when it was serialized, instead, it creates a more reasonable array of data based on the actual size and loading factor data, which is the code that adds the comment above to fetch the new length. The first thing you need to ensure is that the new length is not more than the original length, otherwise there is no meaning, the other is to ensure that the new length is odd, and 5% more than the original. Here also explains why capacity or length need to be odd, Mainly for more uniform mapping. HashMap is set to even number in order to even hash

"Java Source code Analysis" Hashtable source analysis

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.