HashMap
类定义
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
注意:Map接口的定义
public interface Map<K, V> 没有父接口
具体的内部数据存储方式
transient Node<K,V>[] table; //哈希表的本质是一个数组,数组中每一个元素称为一个桶,桶中存放的是键值对
transient Set<Map.Entry<K,V>> entrySet;并不是用于实际存储数据,主要用于针对entrySet和keySet两个视图提供支持
transient int size;当前集合中的元素个数
final float loadFactor; 当前集合的负载因子,当前Map集合中扩容的阈值【负载因子*最大容积】transient int modCount;修改次数,主要用于实现多线程操作时的快死异常
重要的阈值
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 默认的初始化容积
static final int MAXIMUM_CAPACITY = 1 << 30;最大容积值,实际上就是2的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认的负载因子/加载因子
static final int TREEIFY_THRESHOLD = 8;//树化阈值:即链表转成红黑树的阈值,在存储数据时,当链表长度 > 该值时,则将链表转换成红黑树static final int MIN_TREEIFY_CAPACITY = 64;//最小树形化容量阈值:即 当哈希表中的容量 > 该值时,才允许树形化链表 (即将链表转换成红黑树)否则,若桶内元素太多时,则直接扩容,而不是树形化
static final int UNTREEIFY_THRESHOLD = 6;//桶的链表还原阈值:即红黑树转为链表的阈值,当在扩容resize)时(此时HashMap的数据存储位置会重新计算),在重新计算存储位置后,当原有的红黑树内数量< 6时,则将红黑树转换成链表
内部存储的实现
静态内部类用于实现Entry,HahMap中存放的key/value对就被封装为Node对象。其中key就是存放的键值,用于决定具体的存放位置;value是具体存放的数据,hash就是当前Node对象的hash值,next用于指向下一个Node节点(单向链表)
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; 当前节点的hash值
final K key; 该节点存储的key值
V value; 该节点存储的value值
Node<K,V> next; 下一个节点对象 --- 默认使用单向链表结构,当一个链表上的节点数>8并且总存储元素个数>64时自动转换为红黑树
构造器
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; //给属性loadFactor负载因子赋默认值0.75
//并没有对数组进行初始化操作。采用延迟初始化的策略可以节约空间
}
//当指定初始化容积时,调用另外的构造器进行HashMap的初始化,采用默认的负载因子0.75
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//2个参数的构造器,参数1初始化容积大小,参数2负载因子
public HashMap(int initialCapacity, float loadFactor) {
//对初始化容积值进行合法性验证,如果<0则抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor)) 负载因子应该在(0,1]之间的浮点数
//负载因子越小,哈希碰撞的概率越低,也就意味着链表长度越小,但是浪费空间;如果值越大,则节约空间,但是哈希碰撞概率提高,所以一般建议值在(0,1]之间
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
this.loadFactor = loadFactor; //接收合法的负载因子值
this.threshold = tableSizeFor(initialCapacity); 并不是直接使用传入的参数作为初始化数组的长度,而是将传入的整数转换大于等于initialCapacity的最小2的n次方值
例如initialCapacity=5,则真正的初始化容积为8
initialCapacity=10,则真正的初始化容积为16
}
Float.isNaN判断参数是否为一个不是数值 NaN含义为Not a number
public static boolean isNaN(float v) {
return (v != v);
}
//根据传入的初始化容积值求一个合法的初始化容积值
static final int tableSizeFor(int cap) {
int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}