Map 底层为数组+链表 数组内结构为<key,Value>键值对形式,1.8版本后改为数组+链表+红黑树
区别于List,Set集合,Map不继承Collection接口
Map集合特性
- 无序
- 不可重复
- 非线程安全
Map实现类
1)HashMap 无序,非线程安全,可为空
2)LinkedHashMap 集合内元素按插入顺序输出,非线程安全,可为空
3)TreeMap 集合内元素自然排序输出,非线程安全,不可为空(null没有equals和hashcode方法)
4)ConcurrentHashMap 无序,线程安全,可为空
HashMap底层为什么会有链表
HashMap的<Key,Value>键值对以Entry存在于数组中,Map在调用put方法时会初始化一条默认长度16的数组,根据传入的key值获取hash值和数组长度经过一些算法取模可以计算出Entry在数组中的具体下标位置,概率问题,可能下标值相同,于是产生链表。此算法概率问题还可能存在,某下标下链表长度相差很大。
HashMap的扩容
Set的底层是HashMap,所以Set和HashMap的扩容机制相同。可以将Map集合比喻为一个桶,集合元素为桶内元素,当初始化一个Map集合时,只是申明了一个集合,调用put方法才会将集合赋值默认初始大小16,桶容量有限,故Map有阈值,为桶容积的0.75,当桶内元素达到容积的0.75时触发扩容,为原来的容积的2倍。
- Map底层有链表,扩容时为for循环map长度,内while循环链表长度,因为内层循环取链表长度的i不变,故计算链表内元素下标为头插入法,及先插入的元素永远在链表尾,依次像链表头插入直至链表长度。
- 0.75时为节省内存,又能满足Map集合的内存使用的最优阈值数
hashcode冲突
- 链表法:HaspMap链表形成使用的就是链表法,对于Entry取模后计算出的hashcode冲突的情况,Map会先在链表中查找是否有相同的Key,相同则覆盖Value,否则在链表尾插入Entry元素
- 建立公共溢出区:Map分为基本表和溢出表,对于hash冲突的元素,一律填入溢出表中
- 开发地址法
- 线性探测:当hashcode冲突时,会寻找下一个空闲位置存储元素,此方法对于在一次寻找下一空间时若重复遇冲突会加长寻找探测时间,被称作"一次聚集"
- 平方探测:当hashcode冲突时,不在依次寻找空闲空间,而是呈平方路线(增长i^2 倍)寻找,此方法对于hashcode相同时的路线一致,仍存在加长探测时间问题,被称作"二次聚集"
- 再哈希:即当遇到hashcode冲突时,再次计算hashcode,此时hashcode冲突的可能性很小,有效避免了聚集
ConcurrentHashMap
首先,hashmap是非线程安全的,在多线程高并发的场景下,需要实用使用线程安全的map,hashtable是线程安全的,但是hashtable保证线程安全是通过synchronized关键字,被synchronized关键字修饰后,相当于对于想做map修改的线程需要先获取锁,造成性能降低,于是 可以选择使用concurrenthashmap,concurrenthashmap采用分布式锁,使用voliate,final和cas算法减少锁竞争提高性能。
1.7中,concurrenthashmap采用分段锁的思想,每个每段锁称为Segment,hashmap的原理是将<key,value>当作entry进行取hash之后存储到数据中,对于取完hashcode值之后的entry形成链表,而分段锁是将取完hashcode相同的值分段加锁,而每个segment里会再次取hash值形成数组加链表形式;即需要进行两次hash操作,第一次定位到segment,第二次定位到元素所在数组;
1.8中,当随着map的分段增加,锁的粒度也会增加,1.8为了解决锁粒度增加的问题,将分段锁改为数组加链表加红黑树,同时使用synchronized和cas算法保证线程安全;
但同时,加锁只是对于concurrenthashmap的put操作,get操作则不会加锁,是因为内部的node的val值使用了volite关键字修饰
为什么hashmap会将链表转成红黑树
当hashmap的长度大于64,链表大于8时会转成红黑树
树节点占用空间是一般节点的2倍,所以只有当链表长度大于8时才会使用红黑树,根据hashmap的设计,当链表长度大于8的概率是千万分之一,当链表长度大于8时,链表的查询效率很慢,而且对于hashmap来讲,当链表长度不到8时会进行扩容。但当超度小于8时转换为链表是因为红黑树的左旋和右旋的效率远比链表的查询效率高。
而为什么使用红黑树不用二叉树
首先二叉树要求左侧节点小于根节点,右侧节点大于根节点,hashcode取值若均偏向小或偏大时,查询时间长失去转换成树的优势;同时红黑树是弱平衡二叉树,增加删除节点时比平衡二叉树旋转的次数要低;