You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何未超出ArrayList容量时add()仍会调用grow()?

问题:ArrayList指定初始容量后仍触发grow()?

编辑2:补充细节重述问题

我做了一段代码的性能分析,结果让我疑惑:

public class ProfilerTest {
    private static int loops = 1_000_000_000;
    private static int listSize = 10;

    public static void main(String[] args) {
        for (int i = 0; i < loops; i++) {
            var list = new ArrayList<Integer>(listSize);
            for (int j = 0; j < listSize; j++) {
                list.add(j);
            }
            assert list.size() == listSize;
        }
    }
}

性能分析器显示add(Object)调用了grow()。我查看了所用JDK的源码,ArrayList.add(Object)会调用下面这个辅助方法:

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

按逻辑来看,只有当元素数量s等于数组容量时才会触发grow()。但即使我把初始容量设为2*listSize,性能分析器仍然显示grow()被调用了。我到底忽略了什么?


原始问题

我通过性能分析发现,自己的某个方法偶尔会调用ArrayList.grow()——但实际上我是已知所需容量的,理论上不该触发扩容,不过不排除其他方法修改了容量的可能。

出于好奇我做了个基准测试(注:之前看错了数据,误把吞吐量当成耗时,已删除错误结论)。

我现在的疑问只剩:

  • 当把ArrayList填充到其指定的精确容量时,Java真的会调用grow()吗?还是这只是性能分析器的统计误差(比如子例程调用grow()时被错误统计到父调用方)?

下面是我用JMH做的基准测试代码:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 1, time = 3)
@Measurement(iterations = 3, time = 3)
@Fork(value=2)
public class PerformanceTest {
    private int size = 30;

    @Benchmark
    public ArrayList<Integer> MatchingCapacity() {
        var list = new ArrayList<Integer>(size);
        for (int i = 0; i < size; i++) {
            list.add(i);
        }
        return list;
    }

    @Benchmark
    public ArrayList<Integer> MismatchingCapacity() {
        var list = new ArrayList<Integer>(size-1);
        for (int i = 0; i < size; i++) {
            list.add(i);
        }
        return list;
    }

    @Benchmark
    public ArrayList<Integer> OversizedCapacity() {
        var list = new ArrayList<Integer>(size + size/2);
        for (int i = 0; i < size; i++) {
            list.add(i);
        }
        return list;
    }

    @Benchmark
    public ArrayList<Integer> DefaultCapacity() {
        var list = new ArrayList<Integer>();
        for (int i = 0; i < size; i++) {
            list.add(i);
        }
        return list;
    }
}

测试结果:

# JMH version: 1.35
# VM version: JDK 17.0.3.1, Java HotSpot(TM) 64-Bit Server VM, 17.0.3.1+2-LTS-6

size=3
PerformanceTest.DefaultCapacity      thrpt    6  37681589.763 ± 10696982.725  ops/s
PerformanceTest.MatchingCapacity     thrpt    6  54285199.864 ± 20674575.714  ops/s
PerformanceTest.MismatchingCapacity  thrpt    6  21430358.674 ± 11202223.852  ops/s
PerformanceTest.OversizedCapacity    thrpt    6  50519838.911 ± 19575866.550  ops/s

size=30
PerformanceTest.DefaultCapacity      thrpt    6  4253362.573 ±  120027.530  ops/s
PerformanceTest.MatchingCapacity     thrpt    6  8071248.285 ± 5138133.875  ops/s
PerformanceTest.MismatchingCapacity  thrpt    6  5108706.433 ±  562235.033  ops/s
PerformanceTest.OversizedCapacity    thrpt    6  6867041.239 ± 5561825.025  ops/s

size=300
PerformanceTest.DefaultCapacity      thrpt    6  466885.040 ± 486083.752  ops/s
PerformanceTest.MatchingCapacity     thrpt    6  693234.753 ± 268499.890  ops/s
PerformanceTest.MismatchingCapacity  thrpt    6  644182.306 ± 324785.510  ops/s
PerformanceTest.OversizedCapacity    thrpt    6  773775.315 ±  26808.161  ops/s

size=3000
PerformanceTest.DefaultCapacity      thrpt    6  58673.411 ± 23803.125  ops/s
PerformanceTest.MatchingCapacity     thrpt    6  64863.127 ± 33194.262  ops/s
PerformanceTest.MismatchingCapacity  thrpt    6  58921.951 ± 21346.733  ops/s
PerformanceTest.OversizedCapacity    thrpt    6  62658.432 ± 32061.866  ops/s

内容的提问来源于stack exchange,提问作者N4ppeL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 02:50:18