简谈JAVA基础--HashTable

本文探讨了HashTable与HashMap在数据结构、线程安全性、空键值处理及容量调整等方面的差异。详细解析了两者在源码层面的不同实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HashTable与HashMap的区别:

HashTable底层数据结构与HashMap相差不多,可以看一下 简谈JAVA基础--HashMap

HashTable由于每个方法都通过synchronized关键字来进行修饰,所以说是线程安全的。

而HashMap当多个线程同时操作时,没有同步机制所以会导致数据的不安全。

HashTable键值都不可以为Null。而HashMap的key可以为null。

如果在程序中对HashTable进行类似put( null , null )这样的操作时;编译器是可以通过的,但是会在实际运行时抛出异常。

HashMap的长度为2的幂次方,而HashTable为2的幂次方+1。

所以在扩容操作时,HashMap扩展为原先二倍,HashTable为二倍+1。

基本参数:

Entry<?,?>[] table   -- 存放Entry数组(实际上每个Entry就是一个键值对,当Hash值相同时,以链表形式保存)

count   -- 相当于size长度,代表当前已存在键值对个数

threshold    -- 容量:可最大存储键值对个数

loadFactor    -- 加载因子

initialCapacity    -- 实例化长度。

实例化源码:
public Hashtable(int initialCapacity, float loadFactor) {

    // 初始化长度不能小于0

    if (initialCapacity < 0)

        throw new IllegalArgumentException("Illegal Capacity: "+

                initialCapacity);

    // 加载因子不能小于等于0,loadFactor不能为非数字

    if (loadFactor <= 0 || Float.isNaN(loadFactor))

        throw new IllegalArgumentException("Illegal Load: "+loadFactor);


    // 如果初始化长度为0,设置为1

    if (initialCapacity==0)

        initialCapacity = 1;

    // 加载因子赋值

    this.loadFactor = loadFactor;

    // 创建长度为initialCapacity的Entry数组

    table = new Entry<?,?>[initialCapacity];

    // 容量为最大数 与 初始长度 * 加载因子中较小的一个

    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

}


Put操作源码:
public synchronized V put(K key, V value) {

    // Make sure the value is not null

    // value不能为空

    if (value == null) {

        throw new NullPointerException();

    }


    // 将当前table赋给tab[]

    // Makes sure the key is not already in the hashtable.

    Entry<?,?> tab[] = table;

    // 得到key的hash

    int hash = key.hashCode();

    // 获得数组中的索引

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

    @SuppressWarnings("unchecked")

    // 将tab数组中该索引地址内容赋给 entry

    Entry<K,V> entry = (Entry<K,V>)tab[index];

    // 如果有相同key的存在,覆盖value, return

    for(; entry != null ; entry = entry.next) {

        if ((entry.hash == hash) && entry.key.equals(key)) {

            V old = entry.value;

            entry.value = value;

            return old;

        }

    }

    // 没有key值存在则添加键值

    addEntry(hash, key, value, index);

    return null;

}


addEntry源码:
private void addEntry(int hash, K key, V value, int index) {

    modCount++;

    // 原对象table赋给tab[]

    Entry<?,?> tab[] = table;

    // 如果当前键值对个数大于等于 最大可容纳数量

    if (count >= threshold) {

        // Rehash the table if the threshold is exceeded

        // 进行扩容操作

        rehash();

        // 扩容后的table赋给tab

        tab = table;

        // 获取数组索引

        hash = key.hashCode();

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

    }

    // Creates the new entry.

    // 创建一个Enrty赋给数组索引位置

    @SuppressWarnings("unchecked")

    Entry<K,V> e = (Entry<K,V>) tab[index];

    tab[index] = new Entry<>(hash, key, value, e);

    count++;

}


rehash(扩容)源码:

protected void rehash() {

    // 原数组长度

    int oldCapacity = table.length;

    // 原数组

    Entry<?,?>[] oldMap = table;


    // overflow-conscious code

    // 新数组长度为原数组长度 * 2 + 1

    int newCapacity = (oldCapacity << 1) + 1;

    // 如果新长度超过最大值,新长度为最大值

    if (newCapacity - MAX_ARRAY_SIZE > 0) {

        if (oldCapacity == MAX_ARRAY_SIZE)

            // Keep running with MAX_ARRAY_SIZE buckets

            return;

        newCapacity = MAX_ARRAY_SIZE;

    }

    // 定义新数组

    Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

    // 操作统计+1

    modCount++;

    // 新数组可容纳键值对数量

    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

    // 修改当前table引用为新数组

    table = newMap;


    // 将原数组中的元素。转移到新数组当中

    for (int i = oldCapacity ; i-- > 0 ;) {

        for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {

            Entry<K,V> e = old;

            old = old.next;


            int index = (e.hash & 0x7FFFFFFF) % newCapacity;

            e.next = (Entry<K,V>)newMap[index];

            newMap[index] = e;

        }

    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值