Java low-level classes and source code analysis series-HashTable low-level architecture and source code analysis

A few points

  • The underlying data structure of HashTable is based on an array of linked lists (O (n));
  • HashTable does not allow empty key and empty value;
  • HashMap elements are not sorted according to the order in which they are written, but are sorted according to the hash of the Key, taking n modulo (algorithm optimization uses (n-1) & hash)
  • HashTable is a thread-safe class, but it uses synchronized directly on the method, which uses blocking internal locks (locking the entire table) to ensure thread safety, and its concurrency efficiency is low. Consider the Concurrent package, such as ConcurrentHashMap;
  • Hashtable, like HashMap, has two parameters that affect performance, the initial capacity and the load factor, and the load factor is also 0.75 by default;
  • The initial capacity initialCapacity is 11, it is not required to be an exponential multiple of 2, not 16 of HashMap;
  • The hash algorithm directly uses the hashcode of the object (the hash code of the key), without the optimization of HashMap (16-bit XOR);
  • Hashtable creates an array during initialization, HashMap is lazy loading;
  • The positioning algorithm is (e.hash & 0x7FFFFFFF)% tab.length. Since the number of buckets in the HashMap must be an exponential multiple of 2, the method of obtaining the bucket index can be optimized as hash & (length-1);
  • The insertion efficiency of Hashtable is inefficient. Each insertion has to traverse the linked list once. Each time is O (n), the efficiency is lower than HashMap. HashMap is generally O (1) for direct positioning, and O (1) for a small number of elements in the red-black tree LogN), O (n) of a small number of elements in the linked list, the linked list is less than 8;
  • Hashtable read efficiency is also low, each read must traverse the linked list once, each time is O (n), HashMap is almost O (1), directly locate h & (length-1), plus red and black O (LogN), so it is almost O (1);

Class definition

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

Attributes

  • Entry <?,?> [] Table: Entry type array, used to store key-value pairs in Hashtable;
  • int count: how many key-value pairs are stored in the hashtable
  • int threshold: when the count value is greater than this value, the hash table expands the capacity and rehash ()
  • float loadFactor: threshold = initial size of hash table * loadFactor, initial capacity defaults to 11, loadFactor defaults to 0.75
  • int modCount: the number of times this HashTable structure has been modified. This value is incremented every time an element is added, updated, or deleted. Implement the "fail-fast" mechanism. When iterating on the Hashtable in a concurrent collection, if other threads make structural modifications to the Hashtable, the iterator will compare the expectedModCount and modCount to see if they are consistent. If they are not consistent, a ConcurrentModificationException will be thrown.

Constructor

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

    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    public Hashtable() {
        this(11, 0.75f);
    }

    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

modCount consistency

public static void main(String[] args) {
         Hashtable<Integer, String> tb = new Hashtable<Integer,String>();
         tb.put(1, "BUPT");
         tb.put(2, "PKU");
         tb.put(3, "THU");
         Iterator<Entry<Integer, String>> iter = tb.entrySet().iterator();
         while(iter.hasNext()){
             Entry<?, ?> entry = (Entry<?, ?>) iter.next(); //此处会抛出异常
             System.out.println(entry.getValue());
             if("THU ".equals(entry.getValue())){
                 tb.remove(entry.getKey());
             }
         }
    }
/* 输出结果如下:
THU
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.Hashtable$Enumerator.next(Hashtable.java:1367)
    at ali.Main.main(Main.java:16) */

ConcurrentModificationException exception, the value of modCount is updated every time the data in the hashtable is modified, and the next method of the iterator Enumerator <T> will judge modCount! = ExpectedModCount

     public T Next () {
             // first determine whether modCount and equal expectedModCount
             // Since Hashtable object in the main () method changes the value modCount by tb.remove, such that the unequal modCount expectedModCount and thrown
             // solution The way is to replace the tb.remove () method with the iter.remove () method 
            if (modCount! = ExpectedModCount )
                 throw  new ConcurrentModificationException ();
             return nextElement (); 
        } 
// This method modifies modCount and expectedModCount while removing the element The value of 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; @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) { if (e == lastReturned) { modCount++; expectedModCount++; if (prev == null) tab[index] = e.next; else prev.next = e.next; count--; lastReturned = null; return; } } throw new ConcurrentModificationException(); } }

Main method

put

It can be seen that each insert must traverse the linked list once, each time is O (n), the efficiency is lower than HashMap, HashMap is generally O (LogN) in the red-black tree, O (n) in the linked list, direct positioning Is 1

public  synchronized V put (K key, V value) {
         // The value is not allowed to be null 
        if (value == null ) {
             throw  new NullPointerException (); 
        } 

        // Makes sure the key is not already in the hashtable. 
        Entry <? ?,> Tab [] = Table;
         // get key hash 
        int hash = key.hashCode ();
         // get the index corresponding to the hash bucket in the array 
        int index = (hash & 0x7FFFFFFF)% tab.length; 
        @SuppressWarnings ( "unchecked" )
         // Get the head of the linked list in the bucket
        Entry <K, V> entry = (Entry <K, V> ) tab [index];
         // Traversing from the beginning 
        for (; entry! = Null ; entry = entry.next) {
             // Once the hash values ​​are equal and the keys are equal , Replace the old value 
            if ((entry.hash == hash) && entry.key.equals (key)) { 
                V old = entry.value; 
                entry.value = value;
                 return old; 
            } 
        } 
        // if the same key is not found , Then add a new node 
        addEntry (hash, key, value, index);
         return  null ; 
    }

addEntry

private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        //如果尺寸超过了阈值,进行rehash
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

get

public  synchronized V get (Object key) { 
        Entry <?,?> tab [] = table;
         int hash = key.hashCode ();
         int index = (hash & 0x7FFFFFFF)% tab.length;
     // traverse all elements, ha If the value is the same as the key, return 
        for (Entry <?,?> E = tab [index]; e! = Null ; e = e.next) {
             if ((e.hash == hash) && e.key. equals (key)) {
                 return (V) e.value; 
            } 
        } 
        return  null ; 
    }

 

Guess you like

Origin www.cnblogs.com/starcrm/p/12678770.html