Hashmap,Hashtable,ConcurrentHashmap详解

一 HashTable

  • 底层数组+链表实现
  • key和value都不允许为空
  • 线程安全:实现的方式为修改数据锁住整个HashTable,不过效率较低。在这基础上,concurrentHashmap做了优化,采用分段锁机制。

hashtable实现线程安全主要是在方法加synchronized关键字。
如:
在这里插入图片描述
在这里插入图片描述

  • 而且hashtable含有contains(),containsvalue()方法,而hashmap只有containsvalue()方法
  • hashtable的初始容量initiaCapacity=11,负载因子loadFactor=0.75,扩容方式为2initiaCapacity+1倍扩容,扩容时机为,当前的数据size达到initiaCapacityloadFactor=8.25(约等于9)时就会扩容,扩容后capacity=11*2+1=23
  • 计算index的办法,index=(hash&0x7FFFFFFF)%tab.length,先根据hash值与0x7FFFFFFF做&运算,再对桶的长度进行取模,得到在桶中的索引号。

二 HashMap

  • java1.8之前底层数组+链表,1.8之后底层数组+链表+红黑树
  • key和value允许为空,但是只允许一个key为空,多个value可以为空
  • 线程不安全,在多线程情况下会出现数据不一致,死循环问题。可以用concurrentHashmap解决
  • 初始容量initialCapacity=16,每次扩容2initiacapacity。负载因子loadFactor=0.75,扩容时机为当map中元素数量达到initiaCapacityloadFactor=12时发生扩容,最大为2的30次方
  • 计算index的方法:index=hash&(tab.lenth-1),相当于(hash&0x7FFFFFFF)%tab.length作位运算,效率比hashtable高

HashMap基于哈希思想,实现对数据的读写。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来存储值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞时,对象将会储存在链表的下一个节点中。HashMap在每个链表节点中储存键值对对象。当两个不同的键对象的hashcode相同时,它们会储存在同一个bucket位置的链表中,可通过键对象的equals()方法来找到键值对。如果链表大小超过阈值(TREEIFY_THRESHOLD,8),链表就会被改造为树形结构。

在HashMap中,null可以作为键,这样的键只有一个,但可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该key,也可以表示该key所对应的value为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个key,应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null。

HashMap的初始值还要考虑加载因子:

哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链,对Key的查找需要遍历Entry链上的每个元素执行equals()比较。
加载因子:为了降低哈希冲突的概率,默认当HashMap中的键值对达到数组大小的75%时,即会触发扩容。因此,如果预估容量是100,即需要设定100/0.75=134的数组大小。
空间换时间:如果希望加快Key查找的时间,还可以进一步降低加载因子,加大初始大小,以降低哈希冲突的概率。

三 ConccurentHashMap:

  • 底层采用分段数组+链表
  • 通过把整个Map分为N个segment,提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是volatile的,也能保证读到最新的值)
  • Hashtable的synchronized是针对整个表的,即每次锁住整个表让线程独占。Concurrenthashmap允许多个修改操作并发进行,关键在于锁分离技术
  • 有些方法需要跨段,比如size(),containsValue(),这些方法会把所有段都顺序锁住,当完成操作后再释放锁
  • 扩容:段内扩容,当每个段的元素超过该段的75%,该段会进行扩容,而不会对整个map扩容。插入前检测需不需要扩容,避免无效扩容。

在这里插入图片描述
从类图中可以看出来在存储结构中ConcurrentHashMap比HashMap多出了一个类Segment,而Segment是一个可重入锁。

ConcurrentHashMap是使用了锁分段技术来保证线程安全的。

锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。

ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值