hashmap
底层是基于数组+链表实现的,真正存放数据的是entry<k,v>的数组
hashmap如何解决碰撞问题
当不同的对象发生碰撞时,HashMap通过单链表来解决,将新元素加入<链表>表头(头插法),通过next指向原有的元素。单链表在Java中的实现就是对象的引用(复合)。
put方法
- 判断当前数组是否需要初始化
- 如果key为空,则put一个空值进去
- 根据key算出hashcode
- 根据hashcode定位所在的桶(链表)
- 如果桶是链表要遍历里面的hashcode和key和传入的key是否相等,相等则覆盖,并返回原来的值
- 如果桶是空的,说明当前位置没有数据,则新增一个entry对象写入当前位置
get方法
- 根据key来算出hashcode,然后定位到具体的桶中
- 判断该位置是否是链表
- 不是链表就根据key,key的hashcode是否相等来返回值
- 如果是链表则需要遍历知道key及hashcode相等时候返回值
- 啥都没有就返回null
jdk1.8做的优化
对大链表进行了优化,超过阈值后将链表修改为红黑树后查询效率大大增加(当链表较长时转为将链表红黑树),未优化之前是o(n),优化后是o(log n)
会出现的问题
jdk没有对它进行任何的同步操作,所以会出现并发访问的问题,甚至会出现死循环导致系统不可用
解决办法
concurrenHashMap(guava中的cache采用的就是这个)
concurrentHashMap
原理:concurrenthashmap采用了分段锁技术,其中segment继承于reentrantlock。每当一个线程占用锁访问一个segment时,不会影响到其他的segment
jdk1.7和1.8的不同
1.7采用了分段锁,1.8取消了分段锁,改用CAS + synchronized来保证并发安全性
1.8之后在数据结构上做了改动,超过阈值后采用红黑树保证了查询效率,甚至取消了reentrantlock(可重入锁),改成synchronized