应统一使用 new string[0],因其触发jvm专用优化路径,字节码极简、分配开销趋近于零,且避免并发修改、快照不一致等预分配风险,性能更优、语义更清晰。

直接传 new String[0] 比传 new String[list.size()] 更高效,不是直觉上的“预分配更省”,而是 JVM 实际运行时的优化结果。
空数组参数触发 JVM 专用优化路径
HotSpot 对 new T[0] 有特殊识别和内联处理:
- 字节码层面生成极简指令,常被 JIT 编译器直接优化为栈上操作或常量引用
- 避免了运行时计算长度、校验非负性、分配堆内存等步骤
- 多数现代 JDK(8u292+、11+、17+)中,
new String[0]的分配开销趋近于零
预分配 size 数组反而引入不确定性开销
看似精准,实则隐藏三类性能风险:
- 调用
list.size()与后续toArray()内部遍历之间存在时间窗口,若集合被并发修改,可能触发一次冗余扩容复制 - 某些集合实现(如
CopyOnWriteArrayList)的size()是快照值,但迭代过程可能跳过新增元素,导致预分配容量无意义 - 当集合为空时,
new String[list.size()]等价于new String[0],但语义模糊,无法向 JVM 传达“仅需类型信息”的明确意图
数组创建本身不是瓶颈,类型推导与扩容逻辑才是关键
toArray(T[]) 的核心开销不在数组分配,而在运行时类型匹配与可能的扩容决策:
- 传入
new String[0]:方法立即获取组件类型String.class,直接创建正确类型新数组(长度 = size),无条件拷贝 - 传入
new String[n](n ≥ size):需先填充元素,再将剩余位置设为null;若 n < size,仍要新建数组并复制——多一次判断+一次复制 - JVM 对空数组的内存布局更紧凑,GC 扫描压力更低,尤其在高频调用场景下差异可测
实测建议:统一使用 new T[0] 作为标准写法
这不是妥协,而是对 JVM 行为的主动适配:
- 代码简洁,语义清晰:“我要一个 T 类型数组,长度你来定”
- 规避并发、空集合、动态集合等边界情况带来的隐式开销
- 所有主流 JDK 版本均验证其稳定性与性能优势,无需版本适配










