Java 中地图的调整大小行为

Resize behaviour of Maps in Java

我需要知道 Java 中的地图何时放大。为此,我需要一个公式来计算良好的初始容量。

在我的项目中,我需要一张包含大对象的大地图。因此,我想通过指定合适的初始容量来防止调整地图大小。通过反思,我研究了地图的行为。

package com.company;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) {

        Map m  = new HashMap();
        int lastCapacity = 0, currentCapacity = 0;
        for (int i = 1; i <= 100_000; i++) {
            m.put(i,i);
            currentCapacity = getHashMapCapacity(m);
            if (currentCapacity>lastCapacity){
                System.out.println(lastCapacity+" --> "+currentCapacity+" at "+i+" entries.");
                lastCapacity=currentCapacity;
            }
        }
    }

    public static int getHashMapCapacity(Map m){
        int size=0;
        Field tableField = null;
        try {
            tableField = HashMap.class.getDeclaredField("table");
            tableField.setAccessible(true);
            Object[] table = (Object[]) tableField.get(m);
            size = table == null ? 0 : table.length;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return size;
    }
}

输出为:

0 --> 16 个条目。

16 --> 32 共 13 个条目。

32 --> 25 个条目中的 64 个。

64 --> 49 个条目中的 128 个。

128 --> 97 个条目中的 256 个。

256 --> 512 在 193 个条目。

512 --> 1024,共 385 个条目。

1024 --> 769 个条目时为 2048。

2048 --> 4096 在 1537 个条目。

4096 --> 8192 在 3073 个条目。

8192 --> 16384,共 6145 个条目。

16384 --> 32768 在 12289 个条目。

32768 --> 65536 在 24577 个条目。

65536 --> 131072 在 49153 个条目。

131072 --> 98305 个条目中的 262144。

我可以假设地图总是那样吗? Java 7 和 Java 8 之间有什么区别吗?

检查此类行为的最简单方法是查看 openjdk 源代码。这些都是免费提供的,而且相对容易阅读。

在这种情况下,检查 HashMap,您会看到一些详尽的实施说明,这些说明解释了大小调整的工作原理、将什么负载因子用作阈值(它正在驱动您所看到的行为),甚至是如何决定是否使用树木作为垃圾桶。通读一遍,如果不清楚再回来。

代码优化得非常好,扩展是一个非常便宜的操作。我建议在进行任何调整之前使用配置文件来获得一些证据,证明性能问题与扩展有关。

根据文档:

设置其初始容量时应考虑映射中预期的条目数及其负载因子,以尽量减少重新哈希操作的次数。 如果初始容量大于最大条目数除以加载因子,则不会发生重新哈希操作。 https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html