HashMap的数据结构
点进Entry
里面有个Entry<K,V> next,它是个链表
HashMap整体来看是个数组,每个数组对应的是
HashMap就是通过这个next来解决key的hash冲突,通过链表的方式解决hash冲突,而这个链表指的就是LinkedList
HashMap源码里面有四个常量
第一个常量:map的默认长度 2^4
第二个常量:map的最大允许长度 2^30
第三个常量:加载因子 0.75
如果数组长度为16,16*0.75=12,那么到了12的时候,数组就会扩容2倍
HashMap的put方法
第一次向map中添加kv
判断key值是否为空,是可以为空的,但是map会将空值放入链表的首位
addEntry这个方法,把key为null的放入链表首位,hash值为0
如果key不为空,会将key做hash处理,得到int数据值,经过大量算法,例如泊松算法计算得出的
indexFor保证了数组下标永远不会越界,计算出来的hash值会均匀的分布在每个节点上,增加了效率
key的冲突解决(hash碰撞)
如果key的hash值一样,equals也一样,就会产生hash冲突
使用链表解决hash冲突,数组有个next,来解决
HashMap的扩容 resize
点进addEntry,如果长度大于阈值进行扩容,是以前数组的两倍
会将就的值 重新计算hash的值,再放入新的Entry中,这就导致了hashmap的性能相对较低
hashmap的get方法
如果key为null,直接return
get再去值的时候,如果key不为null,会先去计算key的hash值,计算他的位置(数组的位置),和indexFor结合,不用去遍历,提高了查询的效率,这个for循环的作用就是遍历链表,先去用hash判断key的位置,用用equals方法,去定位要取的值
总结:
先用hash定位数组的位置,再去遍历链表,遍历链表的时候,先去用hash判断key的位置,用用equals方法,去定位要取的值
HashMap的不足
Entry放在成员位置,在多线程的情况下,两个map同时向Entry里面存值,可能会导致数组长度不一样,产生线程安全问题
HashMap和HashTable的区别
怎么解决HashMap的线程安全问题
javaUtils里面的concurrent这个包对我们简单常用的数据类型做了一个线程同步、线程安全,这个类下面的所有的成员属性和方法都是线程安全的
在多线程条件下,容易导致死循环,具体表现为CPU使用率100%。因此多线程环境下保证 HashMap 的线程安全性,主要有如下几种方法:
1.替换成Hashtable,Hashtable通过对整个表上锁实现线程安全,因此效率比较低
2.使用Collections类的synchronizedMap方法包装一下。方法如下:
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全的)映射
3.使用ConcurrentHashMap,它使用分段锁来保证线程安全
通过前两种方式获得的线程安全的HashMap在读写数据的时候会对整个容器上锁,而ConcurrentHashMap并不需要对整个容器上锁,它只需要锁住要修改的部分就行了