阅读 JDK 源码:HashMap 扩容总结及图解

本文详细探讨了Java8中HashMap的扩容原理,包括扩容的触发时机、resize方法的源码分析以及链表和树结构在扩容时的迁移算法。通过单元测试和图解方式,展示了扩容过程中节点如何按照新索引分布,确保元素顺序和数据一致性。

在 Java8 中,HashMap 由数组+链表+红黑树组成的。
扩容时,数组容量翻倍,数组中每一个桶中的多个节点(链结构或树结构)都需要 rehash 迁移到新的数组中去。
本文通过阅读 JDK8 中 HashMap 的 resize 方法了解其扩容原理,对桶节点的迁移算法进行单元测试,画图以方便理解。

本文基于 jdk1.8.0_91

1. 扩容的时机

  1. HashMap 中 put 入第一个元素,初始化数组 table。
  2. HashMap 中的元素数量大于阈值 threshold。

threshold = capacity * load factor。
当 size > threshold 时,触发 rehash。

2. 扩容的源码

HashMap 中的 resize 方法主要包含两部分逻辑:

  1. 初始化数组 table,并设置阈值。
  2. 数组容量翻倍,将元素迁移到新数组。
/**
 * Initializes or doubles table size.  If null, allocates in
 * accord with initial capacity target held in field threshold.
 * Otherwise, because we are using power-of-two expansion, the
 * elements from each bin must either stay at same index, or move
 * with a power of two offset in the new table.
 *
 * @return the table
 */
final Node<K,V>[] resize() {
   
   
	Node<K,V>[] oldTab = table;
	int oldCap = (oldTab == null) ? 0 : oldTab.length;
	int oldThr = threshold;
	int newCap, newThr = 0;
	if (oldCap > 0) {
   
    // 第一次进来,table为null,oldCap为0,不会进入这里
		if (oldCap >= MAXIMUM_CAPACITY) {
   
    // 扩容前的数组大小如果已经达到最大(2^30)了
			threshold = Integer.MAX_VALUE; // 取整型最大值(2^31-1),这样以后就不会扩容了
			return oldTab;
		}
		else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && // oldCap翻倍得到newCap
				 oldCap >= DEFAULT_INITIAL_CAPACITY)
			newThr = oldThr << 1; // double threshold
	}
	else if (oldThr > 0) // initial capacity was placed in threshold // 第一次进来,如果手动设置了初始容量initialCapacity,这里为true,则将threshold作为初始容量
		newCap = oldThr;
	else {
   
                  // zero initial threshold signifies using defaults // 如果没有手动设置initialCapacity,则设为默认值16
		newCap = DEFAULT_INITIAL_CAPACITY;
		newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
	}
	if (newThr == 0) {
   
    // 第一次进来,这里必为true,重新计算 threshold = capacity * Load factor
		float ft = (float)newCap * loadFactor;
		newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
				  (int)ft : Integer.MAX_VALUE);
	}
	threshold = newThr;
	@SuppressWarnings({
   
   "rawtypes","unchecked"})
		Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
	table = newTab;
	if (oldTab != null) {
   
    // 对oldTab中所有元素进行rehash。由于每次扩容是2次幂的扩展(指数组长度/桶数量扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置
		for (int j = 0; j < oldCap; ++j) {
   
   
			Node<K,V> e;
			if ((e = oldTab[j]) != null) {
   
    // 数组j位置的元素不为空,需要该位置上的所有元素进行rehash
				oldTab[j] = null;
				if (e.next == null) // 桶中只有一个元素,则直接rehash
					newTab[e.hash & (newCap - 1)] = e;
				else if (e instanceof TreeNode
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值