FrozenGiraffeLiang
FrozenGiraffeLiang

Reputation: 23

Difference between Hashtable and hashmap

I'm doing the two sum question but I want to use hashtable instead of hashmap. When I put the same nums into both hashtable and hashmap. I find out the positions of nums and index in hashtable and hashmap are different. And It seems like hashmap store nums in order according to its bits and hashtable store randomly. Maybe the difference is because of the hash function. I don't know if I'm right. Can someone explain this to me? Thank you! Here's hashmap code and output:

int[] nums = {2, 7, 11, 15,9,1,13};

int target = 9;

int[] result;

Map<Integer, Integer> map = new HashMap<>();

for (int i = 0; i < nums.length; i++) {

    map.put(nums[i], i);
}

System.out.println(map);


if (map.containsKey(target)) {

    System.out.println(map.get(target));
}

Output:

{1=5,2=0,7=1,9=4,11=2,13=6,15=3}

 4

Here's hashtable code and output:

int[] nums = {2, 7, 11, 15,9,1,13};    


int target = 9;    

int[] result;    

Hashtable<Integer, Integer> table = new Hashtable<>();    

for (int i = 0; i < nums.length; i++) {    

    table.put(nums[i], i);} 

System.out.println(table);    


if (table.containsKey(target)) {   
 

System.out.println(table.get(target));}

output:

{9=4,7=1,15=3,13=6,2=0,1=5,11=2}
4

Upvotes: 1

Views: 138

Answers (1)

Sharad Nanda
Sharad Nanda

Reputation: 502

The difference comes due to the implementation of put method in both classes. Below are my observations as per JDK 8.

Hashtable

Hashtable does bit masking with the help of bitwise AND & then calculate the storage index. FYR the method implementation is as follows:

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

Note the index which has been calculated

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

is passed to the addEntry() method. This method does a double hashing when there is a hash collision. FYR the code is as follows:

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

    Entry<?,?> tab[] = table;
    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++;
}

HashMap

The storage implementation is contained in putVal() method. FYR code:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

Since Java 8 on encountering hash collisions objects are stored in a Balanced Binary Tree to enhance the performance by reducing the lookup time to O(log n) over former LinkedList storage(Java 7 & previous) where it was O(n). This is quite visibly different from Hashtable's approach of handling collisions.

HashTable & HashMap even though are mainly based on hashing principle for storage have been implemented quite differently keeping in mind several scenarios like multithreading/concurrent processing etc.

Also, hashcode() method belongs to Object class & returns the object ID which you can check by putting a breakpoint in your IDE & check the value in debug mode.

Upvotes: 1

Related Questions