HashMap 与 HashTable 有什么区别?

1. 线程安全性不同

HashMap 是线程不安全的,它没有对读写等操作进行锁保护;HahTable 是线程安全的,他对所有的读写操作都进行了锁保护。所以在多线程并发的情况下,可以直接使用HashTable,而如果使用 HashMap 需要自己增加同步处理的代码。然后,这个 HashTable 线程安全是因为它的方法大多是同步方法,就是被关键词synchronize所修饰,也就是每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。HashTable实现线程安全的代价就是它的执行效率变低,因为它会锁住整个哈希表,所以现在使用HashTable会比较少,如果说,要使用线程安全的 map 集合,可以使用 ConcurrentHashMap 集合,因为 ConcurrentHashMap 集合使用了分段锁,它并不会对整个数据进行锁操作,所以效率会比HashTable高很多。

总的来说,Hashtable 现在处于一个比较尴尬的位置,如果不考虑线程安全,建议使用 HashMap;如果考虑同步安全问题,建议使用 ConcurrentHashMap

2. 是否提供 contains 方法

HashMap 只有 containsValue 和 containsKey 方法;HashTable 有 contains、containsKey 和 containsValue 三个方法,其中 contains 和 containsValue 方法功能相同

3. key 和 value 是否允许 null 值

在 Hashtable 中,它的 key 和 value 都不允许为null。但是在 HashMap 中,null 可以作为 key 和 value,但是 key 只能有一个为 null,value可以存在多个 null

4. 数组初始化和扩容机制

HashTable 在不指定容量的情况下的默认容量为 11,而 HashMap 为 16,Hashtable 不要求底层数组的容量一定要为 2 的整数次幂,而 HashMap 则要求一定为 2 的整数次幂。 Hashtable 扩容时,将容量变为原来的 2 倍加 1,而 HashMap 扩容时,将容量变为原来的 2 倍

HashTable在不指定容量的情况下的默认容量为11,它的底层的数据结构是数组+链表,默认的加载因子(负载因子/装载因子)是0.75。它的扩容时机就是大于阈值时会自动实现扩容,这个阈值 = Hashtable当前的最大容量*加载因子0.75,也就是当达到最大容的3/4时就自动扩容。这个扩容后的HashTable容量大小 = 旧容量*2+1。然后这个加载因子0.75也不是随便定义的,他是根据泊松分布计算得到的(这个泊松分布主要用于描述单位时间内随机事件发生的次数的概率分布)。如果说加载因子越大,那么元素对空间的利用会更加的充分,但是哈希冲突将会变多,这样会导致链表越来越长,不利于查找操作。反之,加载因子越小,元素在数组中的分布将会过于稀疏,好处就是哈希冲突减少了,但是空间浪费更多了,所以说选择0.75是时间和空间成本上的一种折衷的选择。

HashMap在不指定容量的情况下的默认容量为16,它的底层的数据结构是在JDK8之前是数组+(单向)链表,在JDK8是数组+(单向)链表+(双向)红黑树。然后,这个HashMap和HashTable一样,内部都维护了一个存储数据的Entry数组。当发生冲突时,HashMap采用链表+红黑树的形式解决冲突,它的每一个Entry本质上是一个单向链表。然后,它的添加元素过程是这样的,当添加一个KV对时,会通过哈希函数并根据元素Key计算出Hash地址,然后再在这个hash地址上使用尾插法将元素添加(尾插法虽然插入效率慢,但是可以防止在并发情况下,将链表变成环,出现循环链的情况,导致死循环)。然后,当元素数量超过 0.75*旧的容量大小时,就会实现自动扩容,这个扩容大小为 = 旧容量 * 2。

扩容是会新建了一个HashMap的底层数组,而后调用transfer()方法,就是将旧的HashMap中的全部元素添加到新的HashMap中,并且要重新计算元素在新的数组中的索引位置。在JDK8之前还需要计算哈希值,所以说,这个扩容是一个相当耗时的操作。

然后HashMap链表转红黑树的条件是链表的元素个数大于8(就是从9开始),数组(桶)大小是要大于等于64才会将那一条链表转为红黑树。

然后转为红黑树之后,并不会一直保持红黑树,可能会退化为链表。分两种情况:第一种,当树的节点个数(也就是元素个数)小于等于6时,并且数组执行了resize() 扩容方法,会退化为链表。第二种情况时,当使用remove()方法时,它会调用红黑树的removeTreeNode()方法,如果红黑树根节点为空 / 根结点的左孩子 / 根结点的右子树 / 根结点的左子树的左子树只要有一个为null时,就会发生红黑树退化成链表。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值