首先从一个面试题说起,
题目:jdk8中,一个默认大小(table长度16)的HashMap最少存储几个元素会达到扩容点?
有的可能会说16*0.75=12,有的会说8,让我们一探究竟。
上jdk源码
图1-jdk源码
图2-jdk源码
下面我们看看 treeifyBin 方法内部做了什么
图3-jdk源码
table length 小于 (MIN_TREEIFY_CAPACITY=64) 会进行一次扩容,
resize(),内部为 newCap = oldCap << 1,将老的容量进行左移1位操作,得到结果为原来的2倍。
看了这么多理论分析,那具体结果是什么样的呢,现在我们实际验证一下结果。
下面测试代码假定hashMap为初始的大小,table length = 16 的情况。
本测试用例用到反射机制,取HashMap的私有属性table。
@Test
public void testReSize() throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
List<String> keys = getSameIndexKeys(3);
Field tableField = HashMap.class.getDeclaredField("table");
tableField.setAccessible(true);
HashMap<String, String> hashMap = new HashMap();
for (String key : keys) {
hashMap.put(key, "value");
//打印当前table的长度,看看是否扩容了
System.out.println("map.size: " + hashMap.size() + ", table长度: " + ((Object[]) tableField.get(hashMap)).length);
}
}
/**
* 此方法为获取到相同数组 index 的 keys
*
* @param idx
* @return
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public List<String> getSameIndexKeys(int idx) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List<String> list = new ArrayList();
HashMap hashMap = new HashMap();
int tableSize = 16;
for (int i = 0; i < 1000; i++) {
String key = "a" + i;
//hash值 jdk的计算方式
Method hashMethod = HashMap.class.getDeclaredMethod("hash", Object.class);
hashMethod.setAccessible(true);
int hash = (int) hashMethod.invoke(hashMap, key);
// jdk的计算方式
int index = ((tableSize - 1) & hash);
if (index == idx) {
list.add(key);
}
}
return list;
}
运行结果如下:
说明数组同一位置插入第8个后,再有插入动作开始扩容。
个人浅见,如有不足请大佬不吝指正。