面试时老生常谈的问题:请问HashMap在什么时候扩容?
稍稍看过源码的立马回答:默认装载因子0.75,当size达到总容量的0.75时会扩容。
而事实如此吗?经实验证明,不一定,还需要看JDK的版本。
HashMap中有一个重要的属性叫threshold,扩容临界值,即下一个要调整大小的值(总容量*装载因子)。
一、以JDK1.7为例
查看源码,在put操作时扩容的条件为“(size >= threshold) && (null != table[bucketIndex])”,也就是说还需要同时满足后面条件,那么bucketIndex又是什么呢?直译为“桶的下标”,即下一个存放Entry的桶的位置,这个位置的获取来自方法“indexFor”,如下:
static int indexFor(int h, int length) {//hash值,table的总容量
return h & (length-1);
}
length-1的二进值各位都是1,h & (length-1)的与运算,实质就是h% (length-1),只要hash不相等,理想情况下,需要容量被全部占用时才会扩容。
简而言之,仅当size >= threshold且发生Hash值%(length-1)冲突(或修改已存在的值或)时,才会进行扩容。
二、JDK1.8
查看源码,判断条件如下:
if (++size > threshold)
resize();
当下一个元素添加进来时,size大于扩容临界值时,对HashMap进行扩容操作。因此如果说是1.8及以上版本的JDK,可以回答“默认装载因子0.75,当size达到总容量的0.75时会扩容”。
三、在JDK1.7版本证明上述言论的正确性
1、测试代码,为了促使key的hashcode容量冲突(这里不是指hash冲突,而是key本身就是一个值,目的就是证明key冲突是扩容的第二条件),使用100以内的随机整数作为key。
public static void testHashMap2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
int size = 7;
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(size);
Class<? extends HashMap> class1 = null;
Field field = null;
Entry<Integer, Integer>[] table = null;
int threshold = size;
for(int i=1; i<= 100; i++){
class1 = map.getClass();
field = class1.getDeclaredField("threshold");
field.setAccessible(true);
threshold = field.getInt(map);
System.out.print("添加元素前:i="+i);
System.out.print(" 扩容临界值threshold:"+threshold);
System.out.print(" 键值对大小size:"+map.size());
field = class1.getDeclaredField("table");
field.setAccessible(true);
table = (Entry<Integer, Integer>[])field.get(map);
System.out.println(" 总容量table大小:"+table.length);
map.put((int)(Math.random()*100), (int)(Math.random()*100));
}
}
2、运行可以看到以下内容
(注意,输出以下内容是在插入元素前;如果输出有size重复的情况,说明是同一个key):
(1)第一次扩容,当插入第6个元素时扩容到16(扩容前临界值为6)
(2)第二次扩容,当插入第17个元素时才扩容到32(扩容前临界值为12)
(3)第三次扩容,当插入第25个元素后扩容到64(扩容前临界值为24)
多次运行会有不同的结果,足以说明JDK1.7的扩容点具有一定的随机性。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/u010188178/article/details/86527792
内容来源于网络,如有侵权,请联系作者删除!