HashMap源码浅析
本文摘要
HashMap是Java集合中使用频率很高的一种用于键值对映射数据结构,在很多场景都有着广泛的应用。
本文将着重介绍HashMap在JDK1.8中的实现。
HashMap简介
下图为HashMap继承和实现结构图。我们可以发现,HashMap继承了抽象类AbstractMap,实现了Map接口,除此之外,它实现了Cloneable,Serializable接口,说明HashMap可以实现复制和序列化。
HashMap的基本结构
底层结构概述
HashMap的基本思想就是利用Hash函数将数据分散地放置在不同的位置;在查询时,再次使用Hash函数算出数据所在的位置,从而快速的查找到数据;但是这又有可能出现不同的数据出现在相同的位置(Hash冲突),这就需要仔细设计如何在有Hash冲突的情况下,还能让查询有着较高的效率。
在Java中,不同的JDK版本,HashMap在解决Hash冲突上存在的差别。
JDK1.7:HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的。
JDK1.8:HashMap由数组+链表+红黑树组成的,数组是HashMap的主体,链表和红黑树则是主要为了解决哈希冲突而存在的。当链表长度超过某个长度(默认为8)和数组长度超过某个长度(默认为64)时,链表会转变成红黑树(为了提高查找的效率与性能)。
底层结构主体——table数组
HashMap的主干是一个Node数组,也可以叫做桶。
// HashMap的主干数组,可以看到就是一个Node数组,初始值为空数组{},主干数组的长度一定是2的次幂。
transient Node<K,V>[] table;
Node是HashMap的基本组成单元,每一个Node包含一个key-value键值对。(其实所谓Map其实就是保存了两个对象之间的映射关系的一种集合)
static class Node<K,V> implements Map.Entry<K,V> {
// 对key的hashcode值进行hash运算后得到的值
final int hash;
// 该节点的key值
final K key;
// 该节点的value值
V value;
// 指向下一个节点的指针
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
HashMap的几个重要成员变量
成员变量名称 | 解释 |
---|---|
DEFAULT_INITIAL_CAPACITY | 默认初始大小(16) |
MAXIMUM_CAPACITY | 最大容量(2的30次方) |
DEFAULT_LOAD_FACTOR | 默认负载因子(0.75f) |
TREEIFY_THRESHOLD | 链表转换为红黑树的桶上节点数临界值(8) |
UNTREEIFY_THRESHOLD | 红黑树转换为链表的桶上节点数临界值(6) |
size | 存放元素的个数 |
threshold | 阈值(临界值) |
loadFactor | 负载因子 |
1、这里我们首先要区分CAPACITY和size这两个变量:
①CAPACITY是指HashMap的容量,即数组的长度(桶的数量)
②size是指HashMap内部真正存储的数据数量
2、loadFactor负载因子
负载因子是用来衡量 HashMap 满的程度,表示HashMap的疏密程度。
如果其数值过大,就表明当前HashMap比较拥挤,之后再向HashMap加入数据时,发生Hash冲突的概率会增大。
3、threshold阈值
threshold=容量(capacity)× 负载因子(loadFactor)
当HashMap的实际大小超过临界值时,会进行扩容,以减小HashMap增加数据时Hash冲突的概率。
HashMap的初始化(构造函数)
HashMap共有四个构造函数
1、带有默认初始容量(initialCapacity)和负载因子(loadFactor)的构造函数
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
2、带有默认初始容量(initialCapacity)的构造函数
public HashMap(int initialCapacity) {
// 使用传入的初始容量,默认负载因子,其他的值都使用