前面我已经写过关于HashMap的一些总结,但总感觉不够全面,今天再谈谈HashMap,希望在我复习巩固的同时,对你也有所帮助。
上一篇地址在这Java集合详解
HashMap的内部数据结构
JDK1.7是数组+链表
JDK1.8是数组+链表+红黑树
HashMap在jdk8中相较于jdk7在底层实现方面的不同:
数据结构图
hashMap中几个重要的字段(JDK1.8)
//默认初始容量为16,0000 0001 右移4位 0001 0000为16,主干数组的初始容量为16,而且这个数组
//必须是2的倍数(后面说为什么是2的倍数)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量为int的最大值除2
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认加载因子为0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//阈值,如果主干数组上的链表的长度大于8,链表转化为红黑树
static final int TREEIFY_THRESHOLD = 8;
//hash表扩容后,如果发现某一个红黑树的长度小于6,则会重新退化为链表
static final int UNTREEIFY_THRESHOLD = 6;
//当hashmap容量大于64时,链表才能转成红黑树
static final int MIN_TREEIFY_CAPACITY = 64;
//临界值=主干数组容量*负载因子
int threshold;
hashMap的构造方法:
//initialCapacity为初始容量,loadFactor为负载因子
public HashMap(int initialCapacity, float loadFactor) {
//初始容量小于0,抛出非法数据异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//初始容量最大为MAXIMUM_CAPACITY
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//负载因子必须大于0,并且是合法数字
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
//将初始容量转成2次幂
this.threshold = tableSizeFor(initialCapacity);
}
//tableSizeFor的作用就是,如果传入A,当A大于0,小于定义的最大容量时,
// 如果A是2次幂则返回A,否则将A转化为一个比A大且差距最小的2次幂。
//例如传入7返回8,传入8返回8,传入9返回16
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//调用上面的构造方法,自定义初始容量,负载因子为默认的0.75
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//默认构造方法,负载因子为0.75,初始容量为DEFAULT_INITIAL_CAPACITY=16,初始容量在第一次put时才会初始化
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//传入一个MAP集合的构造方法
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
HashMap的数据插入原理
在前面的文章里,我用文字表达了数据出入的详细过程如图:
现在用一张图来清晰解析:
1、判断数组是否为空,为空进行初始化,在实例化以后,底层创建了长度是16的一维数组Entry[] table。
2、数组不为空,首先,调用key所在类的hashCode()计算key1哈希值,此哈希值经过(n - 1) &
hash计算以后,得到在Entry数组中的存放位置。3、判断table[index] 此位置上是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中
4、如果此位置上存在数据,发生了hash冲突(意味着此位置上存在一个或多个数据(以链表形式存在))
4.1 比较key1和已存在数据的哈希值,如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。
4.2