文档中的这一行,
如果初始容量大于最大条目数除以负载因子,则将不会进行任何哈希操作。
确实可以追溯到在JDK 8(JEP 180)中添加树形实现之前。您可以在JDK
1.6
HashMap文档中看到此文本。实际上,本文是在引入Collections
framework(包括HashMap)时一直追溯到JDK 1.2。您可以在网上找到JDK
1.2文档的非官方版本,或者如果您想亲自查看的话,可以从档案中下载一个版本。
我相信在添加树形实现之前,该文档是正确的。但是,正如您所观察到的,在某些情况下它是不正确的。该策略不仅是如果条目数除以负载因子而超出容量(实际上是表长度),那么可能会发生大小调整。如您所述,如果单个存储桶中的条目数超过TREEIFY_THRESHOLD(当前为8),但表长度小于MIN_TREEIFY_CAPACITY(当前为64),则
也会 发生大小调整。
您可以在
treeifyBin()HashMap
的方法中看到此决定。
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); else if ((e = tab[index = (n - 1) & hash]) != null) {当单个存储桶中的条目超过TREEIFY_THRESHOLD时,到达代码中的这一点。如果表大小等于或大于MIN_TREEIFY_CAPACITY,则此bin被树化;否则,仅调整表的大小。
请注意,在较小的表大小下,这可能会使垃圾箱中的条目多于TREEIFY_THRESHOLD。这并不难证明。首先,一些反射式的HashMap转储代码:
// run with --add-opens java.base/java.util=ALL-UNNAMEDstatic Class<?> classNode;static Class<?> classTreeNode;static Field fieldNodeNext;static Field fieldHashMapTable;static void init() throws ReflectiveOperationException { classNode = Class.forName("java.util.HashMap$Node"); classTreeNode = Class.forName("java.util.HashMap$TreeNode"); fieldNodeNext = classNode.getDeclaredField("next"); fieldNodeNext.setAccessible(true); fieldHashMapTable = HashMap.class.getDeclaredField("table"); fieldHashMapTable.setAccessible(true);}static void dumpMap(HashMap<?, ?> map) throws ReflectiveOperationException { Object[] table = (Object[])fieldHashMapTable.get(map); System.out.printf("map size = %d, table length = %d%n", map.size(), table.length); for (int i = 0; i < table.length; i++) { Object node = table[i]; if (node == null) continue; System.out.printf("table[%d] = %s", i, classTreeNode.isInstance(node) ? "TreeNode" : "BasicNode"); for (; node != null; node = fieldNodeNext.get(node)) System.out.print(" " + node); System.out.println(); }}现在,让我们添加一串都属于同一存储桶的字符串。选择这些字符串,使它们的哈希值(由HashMap计算)均为0 mod 64。
public static void main(String[] args) throws ReflectiveOperationException { init(); List<String> list = List.of( "LBCDD", "IKBNU", "WZQAG", "MKEAZ", "BBCHF", "KRQHE", "ZZMWH", "FHLVH", "ZFLXM", "TXXPE", "NSJDQ", "BXDMJ", "OFBCR", "WVSIG", "HQDXY"); HashMap<String, String> map = new HashMap<>(1, 10.0f); for (String s : list) { System.out.println("===> put " + s); map.put(s, s); dumpMap(map); }}从初始表大小1和可笑的负载因子开始,这会将8个条目放入单独的存储桶中。然后,每次添加另一个条目时,将调整表的大小(加倍),但所有条目最终都位于同一存储桶中。这最终导致表的大小为64,其中一个存储桶具有长度为14的线性节点链(“基本节点”),然后添加下一个条目,最终将其转换为树。
该程序的输出如下:
===> put LBCDDmap size = 1, table length = 1table[0] = BasicNode LBCDD=LBCDD===> put IKBNUmap size = 2, table length = 1table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU===> put WZQAGmap size = 3, table length = 1table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG===> put MKEAZmap size = 4, table length = 1table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ===> put BBCHFmap size = 5, table length = 1table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF===> put KRQHEmap size = 6, table length = 1table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE===> put ZZMWHmap size = 7, table length = 1table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH===> put FHLVHmap size = 8, table length = 1table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH===> put ZFLXMmap size = 9, table length = 2table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH ZFLXM=ZFLXM===> put TXXPEmap size = 10, table length = 4table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH ZFLXM=ZFLXM TXXPE=TXXPE===> put NSJDQmap size = 11, table length = 8table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH ZFLXM=ZFLXM TXXPE=TXXPE NSJDQ=NSJDQ===> put BXDMJmap size = 12, table length = 16table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH ZFLXM=ZFLXM TXXPE=TXXPE NSJDQ=NSJDQ BXDMJ=BXDMJ===> put OFBCRmap size = 13, table length = 32table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH ZFLXM=ZFLXM TXXPE=TXXPE NSJDQ=NSJDQ BXDMJ=BXDMJ OFBCR=OFBCR===> put WVSIGmap size = 14, table length = 64table[0] = BasicNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH ZFLXM=ZFLXM TXXPE=TXXPE NSJDQ=NSJDQ BXDMJ=BXDMJ OFBCR=OFBCR WVSIG=WVSIG===> put HQDXYmap size = 15, table length = 64table[0] = TreeNode LBCDD=LBCDD IKBNU=IKBNU WZQAG=WZQAG MKEAZ=MKEAZ BBCHF=BBCHF KRQHE=KRQHE ZZMWH=ZZMWH FHLVH=FHLVH ZFLXM=ZFLXM TXXPE=TXXPE NSJDQ=NSJDQ BXDMJ=BXDMJ OFBCR=OFBCR WVSIG=WVSIG HQDXY=HQDXY



