JDK1.8源码--Hashtable的扩容机制

前言

今天阅读源码时,在Hashtable的扩容机制看到一个有意思的点,整理之后写出来分享给大家。

正文

Hashtable的扩容机制通过rehash()来实现,如果Hashtable中元素的个数大于临界值时,会调用rehash()来实现扩容。临界值的大小等于Hashtable数组的大小与负载因子相乘,默认的负载因子大小为0.75。

protected void rehash() {
        int oldCapacity = table.length;
        Entry[] oldMap = table;

        // 新数组的容量=旧数组长度*2+1
        int newCapacity = (oldCapacity << 1) + 1;
        // 保证新数组的大小永远小于等于MAX_ARRAY_SIZE
        // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        // 创建新数组
        Entry[] newMap = new Entry[newCapacity];

        modCount++;
        // 计算新的临界值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        // 将旧数组中的元素迁移到新数组中
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry old = (Entry)oldMap[i] ; old != null ; ) {
                Entry e = old;
                old = old.next;

                //计算新数组下标
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                // 头插法的方式迁移旧数组的元素
                e.next = (Entry)newMap[index];
                newMap[index] = e;
            }
        }
    }

Q:Hashtable扩容的数组长度为什么时旧数组长度乘以2加1?

A:Hashtable中数组的长度尽量为素数或者奇数,同时Hashtable采用取模的方式来计算数组下标,这样减少Hash碰撞,计算出来的数组下标更加均匀。但是这样效率会比HashMap利用位运算计算数组下标低。

Q:Hashtable为什么采用头插法的方式迁移数组?

A:采用头插法的方式效率更高。如果采用尾插法需要遍历数组将元素放置到链表的末尾,而采用头插法将结点放置到链表的头部,减少了遍历数组的时间,效率更高。

Q:JDK1.8前HashMap也是采用头插法迁移数据,多线程情况下会造成死循环,JDK1.8对HashMap做出了优化,为什么JDK1.8Hashtable还是采用头插法的方式迁移数据?

A:Hashtable是线程安全的,所以Hashtable不需要考虑并发冲突问题,可以采用效率更高的头插法。

Q:为什么Hashtable渐渐被弃用?

A:Hashtable使用synchronized来实现线程安全,在多并发的情况下效率低下。

你可能感兴趣的:(Java)