**1.HashMap:**
JDK8之前HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里。但是当位于一个桶中的元素较多,及hash知己相等的元素较多时,通过key值依次查找的效率较低。JDK8中采用数组+链表+红黑树实现,当链表长度超过阀值8时,将链表(查询时间复杂度O(n))转为红黑树(时间复杂度O(lg n)),极大的提高了查询效率。
特点:
1.键Key不能重复可以为null,value可以为null,可以重复。
2.底层哈希表,线程不安全,内部排序以哈希值排序,跟插入顺序无关
数据结构:保存数据有两种比较简单的数据结构:数组和链表,数组的特点是寻址容易,插入和删除困难;链表的特点事寻址困难,但插入和删除容易,所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做拉链法的方式可以解决哈希冲突。
继承关系:HashMap继承抽象类AbstractMap,实现Map接口,除次之外还实现了cloneable实现类支持克隆,serializable支持序列化
哈希冲突:哈希是通过对数据进行压缩,提高效率的一种解决方法,但由于通过哈希函数产生的哈希值有限,而数据可能比价多,导致经过哈希函数处理后然然有不同的数据对应相同的值,就产生了哈希冲突
**2.哈希冲突的解决方法:**
1.开放地址方法:(1)线性探测:按顺序决定值时,如果值已存在,则在原来值的基础上加一个单位,直至不发生哈希冲突(2)再平方探测:值存在时,在原来基础上先加1的平方个单位,若还存在则减1的平方个单位(3)伪随机:值存在时,通过随机函数生成一个数,在原来值的基础上加上随机数
2.链式地址法(HashMap的哈希冲突解决方法):对于相同的值使用链表进行连接,使用数组存储每个链表。优点:(1)拉链法处理冲突简单,且无堆积现象,即非同义词绝不会发生冲突,因此平均查找长度较短(2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况(3)开放拉链法为减少冲突,要求装填因子a较小,故当结点规模较大时会浪费很多空间。而拉链法可取a>=1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间。(4)在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相同的结点即可。缺点:指针占用较大空间时,会造成空间浪费,若空间用于增大散列规模进而提高开放地址的效率。
3.建立公共溢出区:在公共溢出区存储所有哈希冲突的数据
4.再哈希法:对于有冲突的值再次进行哈希处理,直至没有哈希冲突。
HashMap的底层数组数据类型为Node<K,V>, 成员变量:初始化数组容量16,最大容量30,负载因子0.75.由链表转红黑树的临界值8,由红黑树转链表的临界值6,扩容临界值=负载因子*容量
3.核心方法
Hash()算法:HashMap通过key的hashCode经过扰动函数处理过后得到hash值,然后通过(n-1)&hash判断当前元素存放的位置(n为数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及key是否相同,如果相同的话,直接覆盖,不相同的就通过拉链法解决冲突
取key的hashCode值、高位运算、取模运算。
扩容机制:向HashMap对象里不停加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。java里数据无法自动扩容,得替换为大的数组
线程安全性:在多线程场景下,应该尽量避免使用线程不安全的HashMap,而使用线程安全的ConcurrentHashMap,在并发多的情况下,多线程使用场景中hashMap可能造成死循环。
负载因子越低,发生哈希冲突的概率就下降了,效率就越高,越浪费内存。
当发生哈希冲突并且size大于阀值时,需要进行数组扩容,扩容时,需要新建一个长度为之前数组两倍的新数组,HashMap的长度一定保持2的次幂
put方法: