HashMap、Hashtable、ConCurrentHashMap

HashMap

底层是使用数组+链表实现的,key和value都可以为null,线程不安全,默认初始值为16,newsize = oldsize*2。需要考虑解决hash冲突,加载因子0.75,空间换时间问题。HashMap是一个散列表,通过“拉链法”解决hash冲突。HashMap数据存储是一个Entry[]数组类型,实际上是一个单向链表。
Hashmap有四个构造函数。
HashMap()默认的构造函数,容量16,加载因子0.75.
HashMap(int capacity)指定容量大小的构造函数,容量自定义,加载因子0.75。
HashMap(int capacity,float loadFactor)指定容量大小以及加载因子的构造函数。
HashMap(Map<? extends K,? extends V> map)包含子MAP的构造函数
HashMap的容量大小一直为2的幂?初始化时会选择大于指定容量的第一个2的幂作为容量。3->4,5->8,9->16。
Put(key,value)时,若key为null,则直接将该键值对放在table[0]中,若key不为null,则计算key对应的hash值,然后添加进hash对应的链表中。若key对应的键值对已经存在,用新的value替换旧的value。
Hashmap遍历通过entrySet()获取hashmap键值对的set集合,通过iterator迭代器遍历set集合。Iterator it = map.entrySet().iterator()。

Hashtable

底层使用数组+链表,也是一个散列表,key和value都不能为null,线程安全,实现线程安全的原理是通过对整个表加锁,导致性能较差,synchronized锁。初始size为11,newsize = oldsize*2+1,插入后扩容,可能导致无效扩容。put(key,value)方法中当value为null时直接抛出NullPointerException异常,key为null时,key.hash方法的源码的hashcode方法会抛出空指针异常。
Hashtable有四个构造方法:
Hashtable()默认构造函数,默认容量是11,加载因子是0.75
Hashtable(int initialCapacity)指定容量构造函数
Hashtable(int initialCapacity,float loadFactory)指定容量和加载因子
Hashtable(Map<? extends K,? extends V> t)包含子map的构造函数
Hashtable的key-value数据存储在Entry[]数组中,是一个单向链表
Hashtable的操作方法中都用synchronized关键字修饰,是线程安全的。
Hashtable遍历通过entrySet()获取键值对的set集合,再通过Iterator迭代器遍历集合。Iterator it = table.entrySet().iterator();

ConCurrentHashmap

底层使用数组+链表实现,key和value都不能为null,线程安全,实现线程安全的方式是锁分离,为每一段进行加锁,并且在段内扩容,在插入前进行检查是否需要扩容。Put(key,value)时抛出空指针异常的原因与hashtable相同。
concurrentHashMap是由Segment(桶)数组结构和HashEntry(节点)数组结构组成。Segment是一种可重入锁ReentrantLock,在concurrentHashMap中扮演锁的角色,HashEntry用于存储键值对数据。一个ConcurrentHashMap中包含一个Segment数组,一个Segment里面包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,对HashEntry数组的数据进行修改时,必须首先获取对应的segment锁。
删除操作时,remove(key),当多个删除操作并发开始时,只要其所在的段不同,就可以同时进行。方法进入时加锁,结束时解锁。删除元素每次删除前,将此节点之前的元素全部进行克隆,尾节点指向删除节点的下一节点,同时长度-1。
get操作不加锁,因为get方法内使用的共享变量都是volatile修饰的,此类字段的写入操作先与读操作。
put操作也是委托给了段的put方法,持有段锁,可以理解为每一个段的segment其实类似与hashtable,新节点插入链表的头结点。
Size(),为了在多线程环境下,保证拿到的数据的准确性,如果把其他put,remove(),clean方法全部锁住,则非常低效。考虑到累加的过程中,count发生变化的几率很小,所以size尝试先2次不加段锁统计每个段的大小并进行累加,如果此过程中count发生了变化,则用加锁的方式计算每个segment的大小。

Hashtable和concurrentHashMap都不能key、value为null,因为在并发环境下,通过map.get(key)得到null,不能判断到底是映射的value为null,还是没有对应的key,用于单线程的map.containKey(key)不能用于多线程环境下,因为若一个线程先get(key),再containkey(key)。期间另一个线程怎么操作这个key都有可能,例如删掉这个key。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wmf_helloWorld

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值