1、准备知识
-
HashMap的底层数据结构
Java语言的基本数据结构可以分为两种,一种是数组,另一种的模拟指针/引用,Java语言中涉及到的数据结构都是这两种的扩充。JDK1.8之前HashMap是数组+链表结合的链表散列。JDK1.8在解决哈希冲突上发生了变化,当链表长度大于阈值/默认8的时候,会将链表转化为红黑树,减少搜索时间。
-
hash算法
我们希望HashMap的元素位置尽量分散,最好是每个位置只有一个元素,这样用hash算法求得该位置后可以直接返回结果,不用再遍历链表/红黑树。
HashMap通过Key的hashCode经过扰动函数(就是hash方法)处理后得到hash值,然后在公式(n-1)& hash (/n是数组长度)判断当前元素的位置是否已存在元素,若已存在则判断新加入的元素和已存在的是否相同,相同则覆盖,不相同则拉链法解决冲突。
//扰动函数 == hash方法 /*JDK1.7 此函数可确保仅在以下方面不同的hashCode每个位位置的恒定倍数有界碰撞次数(默认负荷系数下约为8)。 */ static int hash(int h){ h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } /*JDK1.8的hash方法比JDK1.7的更加简化,JDK1.7的扰动需要4次,JDK1.8的仅需1次*/ static int hash(Object key){ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
-
拉链法:将链表和数组相结合。也就是说创建⼀个链表数组,数组中每⼀格就是⼀个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。下次查询的时候,需要遍历这个链表找到元素,减慢了查询效率。
-
->所有,在存储大规模的数据时,预先指定HashMap的大小为2的幂次方
int capacity = 1; while(capacity < initialCapacity){ capacity <<= 1;//扩大两倍 }
2、HashMap的resize()方法介绍
-
当HashMap中的元素增多时,发生哈希冲突的几率就越来越高,这时候为了提高查询的效率,需要对数组大小进行扩容。
-
当数组的元素规模 > 数组大小 * loadFactory时进行扩容,loadFactory默认是0.75
... map = new HashMap<>(c, loadFactor); //HashMap提供了以上有参构造方法 //initialCapacity是HashMap数组的初始容量 //loadFactor是加载因子
-
什么时候进行resize?1、初始化table的时候 2、数组元素size超出threshold = map.size() * loadFactory
直接扩容 原数组的2倍大小
-
节点在转移的过程中是一个个节点复制还是一串一串的转移?从源码中我们可以看出,扩容时是先找到拆分后处于同一个桶的节点,将这些节点连接好,然后把头节点存入桶中即可
-
话不多说,上源码
-
final Node
-