Java source code reading Hashtable

Type 1 Signatures and Notes

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

This class implements a hash table that maps keys to values. Any non- nullobject can be used as a key or value.

In order to successfully store and retrieve objects from a hash table, the objects used as keys must implement hashCodemethods and equalsmethods.

Similar to HashMap, there are two parameters that affect Hashtable performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is just the capacity when the hash table was created. Please note: In case of "hash collision", a single bucket stores multiple entries, which must be searched in sequence. The load factor (0.75 by default) is the measure that allows the hash table to be satisfied before the capacity is automatically increased. The exact details of when and whether to call the rehash method are implementation-dependent.

All the iterators returned by the iterator method of the collection returned by the "collection view method" of this class are fail-fast (see the HashMap source code reading for the fail-fast mechanism).

Hashtableare synchronous. If a thread-safe implementation is not required, it is recommended to use HashMapinstead Hashtable. If a thread-safe concurrent implementation is required, then it is recommended to use ConcurrentHashMapinstead Hashtable.

I understand the meaning of this sentence in the official document is probably: we try not to use Hashtable, you can use HashMap or ConcurrentHashMap instead of this class. Next, we only interpret the code of basic put, get and remove.

 

2 put method

 1 public synchronized V put(K key, V value) {
 2         // Make sure the value is not null
 3         if (value == null) {
 4             throw new NullPointerException();
 5         }
 6 
 7         // Makes sure the key is not already in the hashtable.
 8         Entry<?,?> tab[] = table;
 9         int hash = key.hashCode();
10         int index = (hash & 0x7FFFFFFF) % tab.length;
11         @SuppressWarnings("unchecked")
12         Entry<K,V> entry = (Entry<K,V>)tab[index];
13         for(; entry != null ; entry = entry.next) {
14             if ((entry.hash == hash) && entry.key.equals(key)) {
15                 V old = entry.value;
16                 entry.value = value;
17                 return old;
18             }
19         }
20 
21         addEntry(hash, key, value, index);
22         return null;
23     }

The put method determines that the value cannot be null at the beginning, otherwise a NullPointerException will be reported , but it does not determine whether the key is empty. Let's write a code to test it:

Hashtable<Integer,String> ht = new Hashtable<>();
ht.put(null, "ouym");

Running the above code will also report NullPointerException , because line9 calls key.hashCode() , if the key is null, of course a null pointer will be reported.

So Hashtable is that both key and value cannot be null.

Next, let's see how Hashtable is located according to the hash value? line10 is as follows

int index = (hash & 0x7FFFFFFF) % tab.length;

Why & 0x7FFFFFFF? (Personal understanding) 0x7FFFFFFF=2 31 , indicating the maximum value of Integer. Under normal circumstances, the hash value should be a non-negative number. In this case, the value of hash & 0x7FFFFFFF is equal to the hash, but in abnormal cases, such as when the hash is out of bounds, the hash becomes a negative number, and hash & 0x7FFFFFFF is equal to the complement of the hash. number (hash=-14, hash & 0x7FFFFFFF=2 31 -14+1).

Hashtable is positioned by % tab.length. At this time, I have to think of the subtlety of HashMap design. There are two performance problems by taking the modulo. First, when the value of tab.length is small, it is determined to locate the low bit of the hash value, and the high bit does not play a role. The direct consequence is that it is easy to conflict. Second, the modulo operation is less efficient than the AND operation (HashMap is the AND operation).

Then check whether the key already exists, if so, overwrite the existing value and return the old value.

Others are no problem, and finally call addEntry to insert new elements.

 

3 get method

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

After locating the corresponding bucket, start traversing the linked list, find the value corresponding to the matching key and return it.

 

4 remove method

 1 public synchronized V remove(Object key) {
 2         Entry<?,?> tab[] = table;
 3         int hash = key.hashCode();
 4         int index = (hash & 0x7FFFFFFF) % tab.length;
 5         @SuppressWarnings("unchecked")
 6         Entry<K,V> e = (Entry<K,V>)tab[index];
 7         for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
 8             if ((e.hash == hash) && e.key.equals(key)) {
 9                 modCount++;
10                 if (prev != null) {
11                     prev.next = e.next;
12                 } else {
13                     tab[index] = e.next;
14                 }
15                 count--;
16                 V oldValue = e.value;
17                 e.value = null;
18                 return oldValue;
19             }
20         }
21         return null;
22     }

If line10 prev is not null, it means that the element to be deleted is not the first one in the linked list, then it is only necessary to point the next element of the element to be deleted to the next of the deleted element. If prev is null, it means that the first element of the linked list is the element to be deleted, as for the head of the linked list to point to the next element (line13).

 

5 Summary

Since there are not many cases of changing classes, only the interpretation of the basic methods is done. But there are still some issues to be aware of

(1) Expansion problem

When the current capacity exceeds the total capacity * load factor (default 0.75), the capacity will be expanded, and the capacity will be doubled. and rehash

(2) Why is it not recommended to use

One reason is the efficiency of implementation. The above code analyzes that the implementation process of positioning is indeed not as efficient as HashMap. Then its only advantage over HashMap thread safety is implemented in ConcurrentHashMap.

There is another reason, Hashtable is based on the ancient Dictionary implementation. Dictionary class is obsolete. New implementations should implement the Map interface (such as HashMap) instead of extending this class.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325843470&siteId=291194637