HashMap继承AbstractMap
- Map
接口:定义了一组通用的操作规范 - Cloneable接口:可以克隆对象(浅拷贝)
- Serializable接口: 对象序列化
HashMap结构图
1. HashMap的属性
1.1 HashMap的重要属性
其中有些属性是有transient关键字修饰的
1.1.1 transient Node[] table;
存储元素(位桶--Node
1.1.2 transient Set> entrySet;
由hashMap 中 Node
1.1.3 transient int size;
存放元素(键-值对)的个数,注意这个不等于数组的长度
1.1.4 transient int modCount;
每次扩容和更改map结构的计数器,fail-fast机制
1.1.5 int threshold;
临界值 当实际大小size(容量*填充因子)超过临界值时,会进行扩容
1.1.6 final float loadFactor;
记录 hashMap 装载因子
1.2 HashMap的静态属性
// 序列号
private static final long serialVersionUID = 362498820763181265L;
// 默认的初始table大小是1<<4(16)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// table的最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认的填充因子(loadFactor)大小
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 当桶(*bucket*)上的结点数大于这个值时会转成红黑树
static final int TREEIFY_THRESHOLD = 8;
// 当桶(*bucket*)上的结点数小于这个值时树转链表
static final int UNTREEIFY_THRESHOLD = 6;
// 桶(*bucket*)中结构转化为红黑树对应的table的最小大小
static final int MIN_TREEIFY_CAPACITY = 64;
2 HashMap的构造函数
2.1 HashMap(int, float)型构造函数
/**
* 指定初始容量及装载因子
*/
public HashMap(int initialCapacity, float loadFactor) {
// 初始容量不能小于0,否则报错
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
// 初始容量不能大于最大值,否则为最大值
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
// 填充因子不能小于或等于0,不能为非数字
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// 初始化填充因子
this.loadFactor = loadFactor;
// 初始化threshold大小
this.threshold = tableSizeFor(initialCapacity);
}
注:此处调用了static final int tableSizeFor(int cap);方法。返回的值是大于等于initialCapacity 的最小2的幂数值
- static final int tableSizeFor(int cap)
/**
* 返回的值是大于等于initialCapacity 的最小2的幂数值
* 若指定初始容量为9,则实际 hashMap 容量为16
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
/**
* 先移位再或运算,最终保证返回值是2的整数幂
* >>> 代表无符号右移,高位取0
*/
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;
}
2.2 HashMap(int)型构造函数
/**
* 仅指定初始容量,装载因子的值采用默认的 0.75
*/
public HashMap(int initialCapacity) {
// 调用HashMap(int, float)型构造函数
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
2.3 HashMap()型构造函数
/**
* 所有参数均采用默认值
*/
public HashMap() {
// 初始化填充因子
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
2.4 HashMap(Map extends K>)型构造函数
/**
* 直接将map放入HashMap中
*/
public HashMap(Map extends K, ? extends V> m) {
// 初始化填充因子
this.loadFactor = DEFAULT_LOAD_FACTOR;
// 将m中的所有元素添加至HashMap中
putMapEntries(m, false);
}
注:此处调用 final void putMapEntries(Map extends K, ? extends V> m, boolean evict)函数将m的所有元素存入本HashMap实例中,此函数在put函数中介绍
3 HashMap的重要函数
3.1 put函数及相关方法
- public V put(K key, V value)
/**
* 指定节点 key,value,向 hashMap 中插入节点
*/
public V put(K key, V value) {
// 注意待插入节点hash值的计算,调用了hash(key) 函数
// 实际调用 putVal() 进行节点的插入
return putVal(hash(key), key, value, false, true);
}
- public void putAll(Map extends K, ? extends V> m)
public void putAll(Map extends K, ? extends V> m) {
putMapEntries(m, true);
}
- final void putMapEntries(Map extends K, ? extends V> m, boolean evict)
/**
* 将m的所有元素存入本HashMap实例中
*/
final void putMapEntries(Map extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
// 判断table是否已经初始化
if (table == null) { // pre-size
// 根据待插入的map 的 size 计算要创建的 hashMap 的容量。
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
// 把要创建的 hashMap 的容量存在 threshold 中
if (t > threshold)
threshold = tableSizeFor(t);
}
// 判断待插入的 map 的 size,若 size 大于 threshold,则先进行 resize()
else if (s > threshold)
resize();
for (Map.Entry extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
// 实际也是调用 putVal 函数进行元素的插入
putVal(hash(key), key, value, false, evict);
}
}
}
注:该方法中调用了final Node
[] resize();函数,此函数为扩容函数具体细节下面介绍;除此之外还调用了static final int tableSizeFor(int cap);上面已经介绍;还有就是static final int hash(Object key) 函数。
- static final int hash(Object key)
/**
* key 的 hash值的计算是通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16)
* 主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候
* 也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
通过上面的put方法看到最后都是调用putVal函数进行插入下面看一下该函数:
- final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict)
/**
* 核心方法
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node[] tab; Node p; int n, i;
// 若table未初始化或者长度为0,调用resize函数进行扩容
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
/* 根据 hash 值确定节点在数组中的插入位置,若此位置没有元素则进行插入,
注意确定插入位置所用的计算方法为 (n - 1) & hash,由于 n 一定是2的幂次,这个操作相当于hash % n 位运算更快*/
if ((p = tab[i = (n - 1) & hash]) == null)
// 空桶,创建新的键值对节点,放入table数组中
tab[i] = newNode(hash, key, value, null);
else {
// 说明待插入位置存在元素 即tab[i]不为空,需要组成单链表或红黑树
Node e; K k;
// 与桶(*bucket*)中首元素相比,如果 hash、key 均等,说明待插入元素和第一个元素相等,直接更新
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// 此时p指的是table[i]中存储的那个Node,如果待插入的节点中hash值和key值在p中已经存在,则将p赋给e
e = p;
// 当前桶(*bucket*)中无该键值对,且桶是红黑树结构,按照红黑树结构插入
else if (p instanceof TreeNode)
e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
else {// 当前桶(*bucket*)中无该键值对,且桶(*bucket*)是链表结构,按照链表结构插入到尾部
// 在链表最末插入结点
for (int binCount = 0; ; ++binCount) {
// 遍历到链表的尾部
if ((e = p.next) == null) {
// 创建链表节点并插入尾部
p.next = newNode(hash, key, value, null);
// 结点数量达到阈值,转化为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// 判断链表中结点的 key 值与插入的元素的 key 值是否相等
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
// 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表将;即p调整为下一个节点
p = e;
}
}
// 表示在桶(*bucket*)中找到key值、hash值与插入元素相等的结点
if (e != null) { // existing mapping for key
// 记录e的value
V oldValue = e.value;
// 判断是否修改已插入节点的value
if (!onlyIfAbsent || oldValue == null)
// 用新值替换旧值
e.value = value;
// 访问后回调
afterNodeAccess(e);// 空函数,由用户根据需要覆盖
return oldValue;
}
}
// 结构性修改
++modCount;
// 键值对数目超过阈值时,进行 resize 扩容
if (++size > threshold)
resize();
// 插入后回调
afterNodeInsertion(evict);// 空函数,由用户根据需要覆盖
return null;
}
注:hash 冲突发生的几种情况:
1.两节点 key 值相同(hash值一定相同),导致冲突;
2.两节点 key 值不同,由于 hash 函数的局限性导致hash 值相同,冲突;
3.两节点 key 值不同,hash 值不同,但 hash 值对数组长度取模后相同,冲突;
其中有红黑树插入方式和链表插入方式,下面先看链表插入:
- final void treeifyBin(Node
[] tab, int hash)
/**
* Replaces all linked nodes in bin at index for given hash unless
* table is too small, in which case resizes instead.
*/
final void treeifyBin(Node[] tab, int hash) {
int n, index; Node e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode hd = null, tl = null;
do {
TreeNode p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
红黑树插入方式:
- final TreeNode
putTreeVal(HashMap map, Node [] tab, int h, K k, V v)
/**
* 该函数是HashMap静态内部类
* static final class TreeNode extends LinkedHashMap.Entry中的方法。
* 主要功能就是以红黑树的方式添加一个键值对
*/
final TreeNode putTreeVal(HashMap map, Node[] tab, int h, K k, V v) {
Class> kc = null;
boolean searched = false;
TreeNode root = (parent != null) ? root() : this;
// 从根节点开始查找合适的插入位置(与二叉搜索树查找过程相同)
for (TreeNode p = root;;) {
int dir, ph; K pk;
if ((ph = p.hash) > h)
dir = -1; // dir小于0,接下来查找当前节点左孩子
else if (ph < h)
dir = 1; // dir大于0,接下来查找当前节点右孩子
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
//进入这个else if 代表 hash 值相同,key 相同
return p;
/*要进入下面这个else if,代表有以下几个含义:
1.当前节点与待插入节点 key 不同, hash 值相同
2.k是不可比较的,即k并未实现 comparable 接口(若 k 实现了comparable 接口,comparableClassFor(k)返回的是k的 class,而不是 null)或 者
compareComparables(kc, k, pk) 返回值为 0(pk 为空 或者 按照 k.compareTo(pk) 返回值为0,返回值为0可能是由于 k的compareTo 方法实现不当引起的,compareTo 判定相等,而上个 else if 中 equals 判定不等)*/
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
// 在以当前节点为根的整个树上搜索是否存在待插入节点(只会搜索一次)
if (!searched) {
TreeNode q, ch;
searched = true;
if (((ch = p.left) != null &&
(q = ch.find(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.find(h, k, kc)) != null))
// 若树中存在待插入节点,直接返回
return q;
}
// 既然k是不可比较的,那我自己指定一个比较方式
dir = tieBreakOrder(k, pk);
}
TreeNode xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
// 找到了待插入的位置,xp 为待插入节点的父节点
// 注意TreeNode节点中既存在树状关系,也存在链式关系,k是不可比较的,即k并未实现 comparable 接口(若 k 实现了comparable 接口,comparableClassFor(k)返回的是k的 class,而并且是双端链表
Node xpn = xp.next;
TreeNode x = map.newTreeNode(h, k, v, xpn);
if (dir <= 0)
xp.left = x;
else
xp.right = x;
xp.next = x;
x.parent = x.prev = xp;
if (xpn != null)
((TreeNode)xpn).prev = x;
// 插入节点后进行二叉树的平衡操作
moveRootToFront(tab, balanceInsertion(root, x));
return null;
}
}
}
3.2 get函数及相关方法
- public V get(Object key)
/**
* 实际上是根据输入节点的 hash 值和 key 值利用getNode 方法进行查找
*/
public V get(Object key) {
Node e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
- final Node
getNode(int hash, Object key)
/**
* HashMap并没有直接提供getNode接口给用户调用,而是提供的get函数,而get函数就是通过getNode来取得元素的。
*/
final Node getNode(int hash, Object key) {
Node[] tab; Node first, e; int n; K k;
// table已经初始化,长度大于0,根据hash寻找table中的项也不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
// 桶中第一项(数组元素)相等
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
// 桶中不止一个结点
if ((e = first.next) != null) {
if (first instanceof TreeNode)
// 若定位到的节点是 TreeNode 节点,则在树中进行查找
return ((TreeNode)first).getTreeNode(hash, key);
do { // 否则在链表中进行查找
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
getNode方法中调用 getTreeNode 函数
- final TreeNode
getTreeNode(int h, Object k)
/**
* 从根节点开始,调用 find 方法进行查找
*/
final TreeNode getTreeNode(int h, Object k) {
return ((parent != null) ? root() : this).find(h, k, null);
}
getTreeNode 函数最终调用 find函数进行红黑树查找。
- final TreeNode
find(int h, Object k, Class> kc)
/**
* Finds the node starting at root p with the given hash and key.
* The kc argument caches comparableClassFor(key) upon first use
* comparing keys.
*/
final TreeNode find(int h, Object k, Class> kc) {
TreeNode p = this;
do {
int ph, dir; K pk;
TreeNode pl = p.left, pr = p.right, q;
// 首先进行hash 值的比较,若不同令当前节点变为它的左孩子或者右孩子
if ((ph = p.hash) > h)
p = pl;
else if (ph < h)
p = pr;
// hash 值相同,进行 key 值的比较
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
// 若k 是可比较的并且k.compareTo(pk) 返回结果不为0可进入下面else if
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
// 若 k 是不可比较的 或者 k.compareTo(pk) 返回结果为0则在整棵树中进行查找,先找右子树,右子树没有再找左子树
else if ((q = pr.find(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
return null;
}
3.3 remove函数
- public V remove(Object key)
/**
* 实际上是根据输入节点的key 值利用removeNode方法进行删除
*/
public V remove(Object key) {
Node e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
- final Node
removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable)
final Node removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node[] tab; Node p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node node = null, e; K k; V v;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
// 待删除元素在桶(*bucket*)中,但不是桶中首元素
else if ((e = p.next) != null) {
// 判断待删除元素是否在红黑树结构的桶中
if (p instanceof TreeNode)
// 查找红黑树
node = ((TreeNode)p).getTreeNode(hash, key);
else {
// 遍历链表,查找待删除元素
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
// p保存待删除节点的前一个节点,用于链表删除操
p = e;
} while ((e = e.next) != null);
}
}
// 1. matchValue为true:表示必须value相等才进行删除操作
// 2. matchValue为false:表示无须判断value,直接根据key进行删除操作
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
// 桶为红黑数结构,删除节点
if (node instanceof TreeNode)
// movable参数用于红黑树操作
((TreeNode)node).removeTreeNode(this, tab, movable);
// 待删除节点是桶链表表头,将子节点放进桶位
else if (node == p)
tab[index] = node.next;
// 待删除节点在桶链表中间
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
// 待删除元素不存在,返回null
return null;
}
3.4 replace 函数
进行replace有两种方案:
- 一种是采用 putVal 的方法,因为很明显 putVal 是能够替换的,但是这里就涉及到了 size ,和 modCount 这两个 field 的变化了,也是要注意的。
- 另一种是 getNode 得到节点,然后替换,所以采用了 getNode 这个方法。
/**
* 根据key和旧的value查找匹配进行替换value
*/
@Override
public boolean replace(K key, V oldValue, V newValue) {
Node e; V v;
if ((e = getNode(hash(key), key)) != null &&
((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
e.value = newValue;
afterNodeAccess(e);
return true;
}
return false;
}
/**
* 根据key查询匹配进行替换value
*/
@Override
public V replace(K key, V value) {
Node e;
if ((e = getNode(hash(key), key)) != null) {
V oldValue = e.value;
e.value = value;
afterNodeAccess(e);
return oldValue;
}
return null;
}
3.5 扩容函数 resize() 之再哈希法
先来看一下resize() 函数
// 保存当前table
Node[] oldTab = table;
// 保存当前table的容量
int oldCap = (oldTab == null) ? 0 : oldTab.length;
// 保存当前阈值
int oldThr = threshold;
// 初始化新的table容量和阈值
int newCap, newThr = 0;
/*
1. resize()函数在size > threshold时被调用。oldCap大于 0 代表原来的 table 表非空,
oldCap 为原表的大小,oldThr(threshold) 为 oldCap × load_factor
*/
if (oldCap > 0) {
// 若旧table容量已超过最大容量,更新阈值为Integer.MAX_VALUE(最大整形值)
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 容量翻倍,使用左移,效率更高
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
// 阈值翻倍
newThr = oldThr << 1; // double threshold
}
/*
2. resize()函数在table为空被调用。oldCap 小于等于 0 且 oldThr 大于0,代表用户创建了一个 HashMap,但是使用的构造函数为
HashMap(int initialCapacity, float loadFactor) 或 HashMap(int initialCapacity)
或 HashMap(Map extends K, ? extends V> m),导致 oldTab 为 null,oldCap 为0, oldThr 为用户指定的 HashMap的初始容量。
*/
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
/*
3. resize()函数在table为空被调用。oldCap 小于等于 0 且 oldThr 等于0,用户调用 HashMap()构造函数创建的 HashMap,所有值均采用默认值,oldTab(Table)表为空,oldCap为0,oldThr等于0,
*/
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// 新阈值为0
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
// 初始化table
Node[] newTab = (Node[])new Node[newCap];
table = newTab;
if (oldTab != null) {
// 把 oldTab 中的节点 reHash 到 newTab 中去
for (int j = 0; j < oldCap; ++j) {
Node e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
// 若节点是单个节点,直接在 newTab 中进行重定位
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
// 若节点是 TreeNode 节点,要进行 红黑树的 rehash 操作
else if (e instanceof TreeNode)
((TreeNode)e).split(this, newTab, j, oldCap);
// 若是链表,进行链表的 rehash 操作
else { // preserve order
Node loHead = null, loTail = null;
Node hiHead = null, hiTail = null;
Node next;
// 将同一桶中的元素根据(e.hash & oldCap)是否为0进行分割,分成两个不同的链表,完成rehash
do {
next = e.next;
// 根据算法 e.hash & oldCap 判断节点位置rehash 后是否发生改变
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) { // 原bucket位置的尾指针不为空(即还有node)
loTail.next = null; // 链表最后得有个null
newTab[j] = loHead; // 链表头指针放在新桶的相同下标(j)处
}
if (hiTail != null) {
hiTail.next = null;
// rehash 后节点新的位置一定为原来基础上加上 oldCap
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
- 什么时候扩容:通过HashMap源码可以看到是在put操作时,即向容器中添加元素时,判断当前容器中元素的个数是否达到阈值(当前数组长度乘以加载因子的值)的时候,就要自动扩容了。
- 扩容(resize):其实就是重新计算容量;而这个扩容是计算出所需容器的大小之后重新定义一个新的容器,将原来容器中的元素放入其中。
经过rehash之后,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置
下面根据图来解释一下再哈希法
从上图可以看出其HashMap的元素根据hashcode 计算下标索引。
- (a)表示扩容前的key1和key2两种key确定索引位置的示例
- (b)表示扩容后的key1和key2两种key确定索引位置的示例
- hash1是key1对应的哈希与高位运算结果
- 元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化,如下图
因此我们在扩容时只需要看一下新增的 bit是1还是0,如果是0则索引不变,如果是 1 则索引变为:“原索引+oldCap”
下图为16扩充为32的resize示意图:
浅蓝色代表新增 bit 为0 ,扩容后索引不变,灰绿色代表新增 bit 为1 扩容后索引变为“原索引+oldCap”
3.6 Serializable方法
3.6.1 writeObject方法
/**
* 将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流对象中
*/根据写入方式读出,将HashMap的“总的容量,实际容量,所有的Entry”依次读出
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
int buckets = capacity();
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();
s.writeInt(buckets);
s.writeInt(size);
internalWriteEntries(s);
}
3.6.2 readObject方法
/**
* 根据写入方式读出,将HashMap的“总的容量,实际容量,所有的Entry”依次读出
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
reinitialize();
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
s.readInt(); // Read and ignore number of buckets
int mappings = s.readInt(); // Read number of mappings (size)
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);
else if (mappings > 0) { // (if zero, use defaults)
// Size the table using given load factor only if within
// range of 0.25...4.0
float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
float fc = (float)mappings / lf + 1.0f;
int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
DEFAULT_INITIAL_CAPACITY :
(fc >= MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY :
tableSizeFor((int)fc));
float ft = (float)cap * lf;
threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
(int)ft : Integer.MAX_VALUE);
@SuppressWarnings({"rawtypes","unchecked"})
Node[] tab = (Node[])new Node[cap];
table = tab;
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
}
}
3.6.3 internalWriteEntries方法
/**
* 仅被writeObject调用,确保HashMap的键和值被序列化的存储顺序
*/
void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
Node[] tab;
if (size > 0 && (tab = table) != null) {
for (int i = 0; i < tab.length; ++i) {
for (Node e = tab[i]; e != null; e = e.next) {
s.writeObject(e.key);
s.writeObject(e.value);
}
}
}
}
3.7 其他方法
- public void clear()
/**
* 删除此Map的所有映射
*/
public void clear() {
Node[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
- public Object clone()
/**
* 返回此 HashMap实例的浅拷贝:键和值本身不被克隆
*/
@SuppressWarnings("unchecked")
@Override
public Object clone() {
HashMap result;
try {
result = (HashMap)super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
result.reinitialize();
result.putMapEntries(this, false);
return result;
}
- public boolean containsKey(Object key)
/**
* 如果此映射包含指定键的映射,则返回 true
*/
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
- public boolean containsValue(Object value)
/**
* 如果此Map将一个或多个键映射到指定的值,则返回 true 。
*/
public boolean containsValue(Object value) {
Node[] tab; V v;
if ((tab = table) != null && size > 0) {
for (int i = 0; i < tab.length; ++i) {
for (Node e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}
- public Set
> entrySet()
/**
* 返回此Map中包含的映射的Set视图
*/
public Set> entrySet() {
Set> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
- public Set
> entrySet()
/**
* 返回到指定键所映射的值,或 defaultValue如果此映射包含该键的映射
*/
@Override
public V getOrDefault(Object key, V defaultValue) {
Node e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
4. HashMap的内部类
4.1 Node内部类
/**
* 继承自 Map.Entry这个内部接口,它就是存储一对映射关系的最小单元,也就是说key,value实际存储在Node中
*/
static class Node implements Map.Entry {
// 结点的哈希值,不可变
final int hash;
final K key;
V value;
// 指向下一个节点
Node next;
Node(int hash, K key, V value, Node next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
// 由直接实现 变为 调用Object的HashCode,实际是一样的
public final int hashCode() {
//按位异或^不同为真,数a两次异或同一个数b(a=a^b^b)仍然为原值a
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
// 调用Object的equals
public final boolean equals(Object o) {
// 内存地址
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry,?> e = (Map.Entry,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
4.2 TreeNode内部类
/**
* Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
* extends Node) so can be used as extension of either regular or
* linked node.
*/
static final class TreeNode extends LinkedHashMap.Entry {
TreeNode parent; // red-black tree links
TreeNode left;
TreeNode right;
TreeNode prev; // 节点的前一个节点
boolean red; //true表示红节点,false表示黑节点
TreeNode(int hash, K key, V val, Node next) {
super(hash, key, val, next);
}
/**
* 获取红黑树的根
*/
final TreeNode root() {
for (TreeNode r = this, p;;) {
if ((p = r.parent) == null)
return r;
r = p;
}
}
/**
* 确保root是桶中的第一个元素,将root移到桶中的第一个【平衡思想】
*/
static void moveRootToFront(Node[] tab, TreeNode root) {
int n;
if (root != null && tab != null && (n = tab.length) > 0) {
int index = (n - 1) & root.hash;
TreeNode first = (TreeNode)tab[index];
if (root != first) {
Node rn;
tab[index] = root;
TreeNode rp = root.prev;
if ((rn = root.next) != null)
((TreeNode)rn).prev = rp;
if (rp != null)
rp.next = rn;
if (first != null)
first.prev = root;
root.next = first;
root.prev = null;
}
assert checkInvariants(root);
}
}
/**
* 查找hash为h,key为k的节点
*/
final TreeNode find(int h, Object k, Class> kc) {
TreeNode p = this;
do {
int ph, dir; K pk;
TreeNode pl = p.left, pr = p.right, q;
if ((ph = p.hash) > h)
p = pl;
else if (ph < h)
p = pr;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
else if ((q = pr.find(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
return null;
}
/**
* 获取树节点,通过根节点查找 .
*/
final TreeNode getTreeNode(int h, Object k) {
return ((parent != null) ? root() : this).find(h, k, null);
}
/**
* 比较2个对象的大小
*/
static int tieBreakOrder(Object a, Object b) {
int d;
if (a == null || b == null ||
(d = a.getClass().getName().
compareTo(b.getClass().getName())) == 0)
d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
-1 : 1);
return d;
}
/**
* 将链表转为二叉树
*/
final void treeify(Node[] tab) {
TreeNode root = null;
for (TreeNode x = this, next; x != null; x = next) {
next = (TreeNode)x.next;
x.left = x.right = null;
if (root == null) {
x.parent = null;
x.red = false;
root = x;
}
else {
K k = x.key;
int h = x.hash;
Class> kc = null;
for (TreeNode p = root;;) {
int dir, ph;
K pk = p.key;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
TreeNode xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
root = balanceInsertion(root, x);
break;
}
}
}
}
moveRootToFront(tab, root);
}
/**
* 将二叉树转为链表
*/
final Node untreeify(HashMap map) {
Node hd = null, tl = null;
for (Node q = this; q != null; q = q.next) {
Node p = map.replacementNode(q, null);
if (tl == null)
hd = p;
else
tl.next = p;
tl = p;
}
return hd;
}
/**
* 添加一个键值对
*/
final TreeNode putTreeVal(HashMap map, Node[] tab,
int h, K k, V v) {
Class> kc = null;
boolean searched = false;
TreeNode root = (parent != null) ? root() : this;
for (TreeNode p = root;;) {
int dir, ph; K pk;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0) {
if (!searched) {
TreeNode q, ch;
searched = true;
if (((ch = p.left) != null &&
(q = ch.find(h, k, kc)) != null) ||
((ch = p.right) != null &&
(q = ch.find(h, k, kc)) != null))
return q;
}
dir = tieBreakOrder(k, pk);
}
TreeNode xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
Node xpn = xp.next;
TreeNode x = map.newTreeNode(h, k, v, xpn);
if (dir <= 0)
xp.left = x;
else
xp.right = x;
xp.next = x;
x.parent = x.prev = xp;
if (xpn != null)
((TreeNode)xpn).prev = x;
moveRootToFront(tab, balanceInsertion(root, x));
return null;
}
}
}
/**
* 删除指定节点
*/
final void removeTreeNode(HashMap map, Node[] tab,
boolean movable) {
int n;
if (tab == null || (n = tab.length) == 0)
return;
int index = (n - 1) & hash;
TreeNode first = (TreeNode)tab[index], root = first, rl;
TreeNode succ = (TreeNode)next, pred = prev;
if (pred == null)
tab[index] = first = succ;
else
pred.next = succ;
if (succ != null)
succ.prev = pred;
if (first == null)
return;
if (root.parent != null)
root = root.root();
if (root == null || root.right == null ||
(rl = root.left) == null || rl.left == null) {
tab[index] = first.untreeify(map); // too small
return;
}
TreeNode p = this, pl = left, pr = right, replacement;
if (pl != null && pr != null) {
TreeNode s = pr, sl;
while ((sl = s.left) != null) // find successor
s = sl;
boolean c = s.red; s.red = p.red; p.red = c; // swap colors
TreeNode sr = s.right;
TreeNode pp = p.parent;
if (s == pr) { // p was s's direct parent
p.parent = s;
s.right = p;
}
else {
TreeNode sp = s.parent;
if ((p.parent = sp) != null) {
if (s == sp.left)
sp.left = p;
else
sp.right = p;
}
if ((s.right = pr) != null)
pr.parent = s;
}
p.left = null;
if ((p.right = sr) != null)
sr.parent = p;
if ((s.left = pl) != null)
pl.parent = s;
if ((s.parent = pp) == null)
root = s;
else if (p == pp.left)
pp.left = s;
else
pp.right = s;
if (sr != null)
replacement = sr;
else
replacement = p;
}
else if (pl != null)
replacement = pl;
else if (pr != null)
replacement = pr;
else
replacement = p;
if (replacement != p) {
TreeNode pp = replacement.parent = p.parent;
if (pp == null)
root = replacement;
else if (p == pp.left)
pp.left = replacement;
else
pp.right = replacement;
p.left = p.right = p.parent = null;
}
TreeNode r = p.red ? root : balanceDeletion(root, replacement);
if (replacement == p) { // detach
TreeNode pp = p.parent;
p.parent = null;
if (pp != null) {
if (p == pp.left)
pp.left = null;
else if (p == pp.right)
pp.right = null;
}
}
if (movable)
moveRootToFront(tab, r);
}
/**
* 这个函数的功能是对红黑树进行 rehash 操作 即将结点太多的桶分割
*/
final void split(HashMap map, Node[] tab, int index, int bit) {
TreeNode b = this;
// Relink into lo and hi lists, preserving order
TreeNode loHead = null, loTail = null;
TreeNode hiHead = null, hiTail = null;
int lc = 0, hc = 0;
// 由于 TreeNode 节点之间存在双端链表的关系,可以利用链表关系进行 rehash
for (TreeNode e = b, next; e != null; e = next) {
next = (TreeNode)e.next;
e.next = null;
if ((e.hash & bit) == 0) {
if ((e.prev = loTail) == null)
loHead = e;
else
loTail.next = e;
loTail = e;
++lc;
}
else {
if ((e.prev = hiTail) == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
++hc;
}
}
//rehash 操作之后注意对根据链表长度进行 untreeify 或 treeify 操作
if (loHead != null) {
if (lc <= UNTREEIFY_THRESHOLD)
tab[index] = loHead.untreeify(map);
else {
tab[index] = loHead;
if (hiHead != null) // (else is already treeified)
loHead.treeify(tab);
}
}
if (hiHead != null) {
if (hc <= UNTREEIFY_THRESHOLD)
tab[index + bit] = hiHead.untreeify(map);
else {
tab[index + bit] = hiHead;
if (loHead != null)
hiHead.treeify(tab);
}
}
}
/* ------------------------------------------------------------ */
// Red-black tree methods, all adapted from CLR
// 左旋转
static TreeNode rotateLeft(TreeNode root,
TreeNode p) {
TreeNode r, pp, rl;
if (p != null && (r = p.right) != null) {
if ((rl = p.right = r.left) != null)
rl.parent = p;
if ((pp = r.parent = p.parent) == null)
(root = r).red = false;
else if (pp.left == p)
pp.left = r;
else
pp.right = r;
r.left = p;
p.parent = r;
}
return root;
}
// 右旋转
static TreeNode rotateRight(TreeNode root,
TreeNode p) {
TreeNode l, pp, lr;
if (p != null && (l = p.left) != null) {
if ((lr = p.left = l.right) != null)
lr.parent = p;
if ((pp = l.parent = p.parent) == null)
(root = l).red = false;
else if (pp.right == p)
pp.right = l;
else
pp.left = l;
l.right = p;
p.parent = l;
}
return root;
}
// 保证插入后平衡,共5种插入情况
static TreeNode balanceInsertion(TreeNode root,
TreeNode x) {
}
// 删除后调整平衡 ,共6种删除情况
static TreeNode balanceDeletion(TreeNode root,
TreeNode x) {
for (TreeNode xp, xpl, xpr;;) {
if (x == null || x == root)
return root;
else if ((xp = x.parent) == null) {
x.red = false;
return x;
}
else if (x.red) {
x.red = false;
return root;
}
else if ((xpl = xp.left) == x) {
if ((xpr = xp.right) != null && xpr.red) {
xpr.red = false;
xp.red = true;
root = rotateLeft(root, xp);
xpr = (xp = x.parent) == null ? null : xp.right;
}
if (xpr == null)
x = xp;
else {
TreeNode sl = xpr.left, sr = xpr.right;
if ((sr == null || !sr.red) &&
(sl == null || !sl.red)) {
xpr.red = true;
x = xp;
}
else {
if (sr == null || !sr.red) {
if (sl != null)
sl.red = false;
xpr.red = true;
root = rotateRight(root, xpr);
xpr = (xp = x.parent) == null ?
null : xp.right;
}
if (xpr != null) {
xpr.red = (xp == null) ? false : xp.red;
if ((sr = xpr.right) != null)
sr.red = false;
}
if (xp != null) {
xp.red = false;
root = rotateLeft(root, xp);
}
x = root;
}
}
}
else { // symmetric
if (xpl != null && xpl.red) {
xpl.red = false;
xp.red = true;
root = rotateRight(root, xp);
xpl = (xp = x.parent) == null ? null : xp.left;
}
if (xpl == null)
x = xp;
else {
TreeNode sl = xpl.left, sr = xpl.right;
if ((sl == null || !sl.red) &&
(sr == null || !sr.red)) {
xpl.red = true;
x = xp;
}
else {
if (sl == null || !sl.red) {
if (sr != null)
sr.red = false;
xpl.red = true;
root = rotateLeft(root, xpl);
xpl = (xp = x.parent) == null ?
null : xp.left;
}
if (xpl != null) {
xpl.red = (xp == null) ? false : xp.red;
if ((sl = xpl.left) != null)
sl.red = false;
}
if (xp != null) {
xp.red = false;
root = rotateRight(root, xp);
}
x = root;
}
}
}
}
}
/**
* 检测是否符合红黑树
*/
static boolean checkInvariants(TreeNode t) {
TreeNode tp = t.parent, tl = t.left, tr = t.right,
tb = t.prev, tn = (TreeNode)t.next;
if (tb != null && tb.next != t)
return false;
if (tn != null && tn.prev != t)
return false;
if (tp != null && t != tp.left && t != tp.right)
return false;
if (tl != null && (tl.parent != t || tl.hash > t.hash))
return false;
if (tr != null && (tr.parent != t || tr.hash < t.hash))
return false;
if (t.red && tl != null && tl.red && tr != null && tr.red)
return false;
if (tl != null && !checkInvariants(tl))
return false;
if (tr != null && !checkInvariants(tr))
return false;
return true;
}
}
注:将树节点插入单独提出来进行分析
// 保证插入后平衡,共5种插入情况
static TreeNode balanceInsertion(TreeNode root,
TreeNode x) {
x.red = true;
// 死循环加变量定义
for (TreeNode xp, xpp, xppl, xppr;;) {
// xp X父节点, XPP X的祖父节点, XPPL 祖父左节点 XXPR 祖父右节点
if ((xp = x.parent) == null) {
x.red = false;
return x;
}
// 如果父节点是黑色, 或者XP父节点是空,直接返回
else if (!xp.red || (xpp = xp.parent) == null)
return root;
// 下面的代码就和上面的很treeMap像了,
if (xp == (xppl = xpp.left)) {
/* 第一种情况, 赋值xppl,父节点是左节点的情况,下面这种
G
P(RED) U
*/
if ((xppr = xpp.right) != null && xppr.red) {
// 如果红树的情况,这种情况,对应下面 图:情况一
/*
G
P(RED) U(RED)
X
*/
// 改变其颜色
xppr.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
// 黑树的情况
/*
G
P(RED) U(BLACK)
*/
if (x == xp.right) {
// 如果插入节点在右边 这种对应下面 图:情况二
/*
G
P(RED) U(BLACK)
X
*/
// 需要进行左旋
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
// 左旋后情况都是这种了,对应下面 图:情况三
/*
G
P(RED) U(BLACK)
X
*/
// 到这,X只能是左节点了,而且P是红色,U是黑色的情况
if (xp != null) {
// 把P改成黑色,G改成红色, 以G为节点进行右旋
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateRight(root, xpp);
}
}
}
}
else {
/* 父节点在右边的
G
U P(RED)
*/
// 获取U
if (xppl != null && xppl.red) {
// 红父红树的情况
/*
G
U(RED) P(RED)
*/
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
}
else {
if (x == xp.left) {
// 如果插入的X是右节点
/*
G
U(BLACK) P(RED)
X
*/
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
// 右旋后
/*
G
U(BLACK) P(RED)
X
*/
if (xp != null) {
// 把P改成黑色,G改成红色
xp.red = false;
if (xpp != null) {
xpp.red = true;
// 以G节点左旋
root = rotateLeft(root, xpp);
}
}
}
}
}
}
红黑树插入流程图
5.HashMap的迭代器
5.1 基础迭代器--HashIterator
HashIterator是一个抽象类,封装了迭代器内部工作的一些操作。
/**
* HashIterator是HashMap的一个内部抽象类,为HashMap的迭代器
*/
abstract class HashIterator {
// 下一个结点
Node next; // next entry to return
// 当前结点
Node current; // current entry
// 期望的修改次数 fast-fail机制
int expectedModCount; // for fast-fail
// 当前桶索引
int index; // current slot
/**
* next将表示第一个非空桶中的第一个结点,index将表示下一个桶。
*/
HashIterator() {
expectedModCount = modCount;
Node[] t = table;
current = next = null;
index = 0;
// table不为空并且大小大于0
if (t != null && size > 0) { // advance to first entry
// 找到table数组中第一个存在的结点,即找到第一个具有元素的桶
do {} while (index < t.length && (next = t[index++]) == null);
}
}
// 是否存在下一个结点
public final boolean hasNext() {
return next != null;
}
/**
* nextNode函数屏蔽掉了桶的不同所带来的差异,就好像所有元素在同一个桶中,依次进行遍历。
*/
final Node nextNode() {
Node[] t;
// 记录next结点
Node e = next;
// 若在遍历时对HashMap进行结构性的修改则会抛出异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 下一个结点为空,抛出异常
if (e == null)
throw new NoSuchElementException();
// 如果下一个结点为空,并且table表不为空;表示桶中所有结点已经遍历完,需寻找下一个不为空的桶
if ((next = (current = e).next) == null && (t = table) != null) {
// 找到下一个不为空的桶
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node p = current;
// 当前结点为空,抛出异常
if (p == null)
throw new IllegalStateException();
// 若在遍历时对HashMap进行结构性的修改则会抛出异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// 当前结点为空
current = null;
K key = p.key;
// 移除结点
removeNode(hash(key), key, null, false, false);
// 赋最新值
expectedModCount = modCount;
}
}
5.2 键迭代器--KeyIterator
/**
* KeyIterator类是键迭代器,继承自HashIterator,实现了Iterator接口,可以对HashMap中的键进行遍历。
*/
final class KeyIterator extends HashIterator
implements Iterator {
public final K next() { return nextNode().key; }
}
5.3 值迭代器--ValueIterator
/**
* ValueIterator类是值迭代器,继承自HashIterator,实现了Iterator接口,与KeyIterator类似,对值进行遍历
*/
final class ValueIterator extends HashIterator
implements Iterator {
public final V next() { return nextNode().value; }
}
5.4 结点迭代器--EntryIterator
/**
* EntryIterator类是结点迭代器,继承自HashIterator,实现了Iterator接口,与KeyIterator、ValueIterator类似,对结点进行遍历。
*/
final class EntryIterator extends HashIterator
implements Iterator> {
public final Map.Entry next() { return nextNode(); }
}
致谢:
JAVA中的数据结构 - 真正的去理解红黑树
HashMap的扩容机制---resize()