toArray(new String[0]) 更快,因JVM对空数组重载做了优化:直接用Arrays.copyOf+反射扩容,绕过类型数组重复创建;而非空数组需类型校验、边界检查及可能的复制。
![java中如何将list转换为固定长度的数组_toarray(new string[0])的内存分配优化](https://img.php.cn/upload/article/000/969/633/177353515148428.jpeg)
为什么 toArray(new String[0]) 比 toArray(new String[list.size()]) 更快
因为 JVM 对空数组的 toArray 重载做了专门优化:它能直接用 Arrays.copyOf + Object[] 反射扩容,绕过目标类型数组的重复创建。而传入已知长度的数组(如 new String[list.size()])时,ArrayList 会先尝试写入,再判断是否填满——若刚好填满,就直接返回;但多数情况下,JVM 仍需在运行时检查数组类型、长度、协变性,触发额外边界校验和可能的复制。
- HotSpot 中,
toArray(new T[0])走的是高度内联的 fast-path,底层调用Array.newInstance+System.arraycopy,一次完成 - 传非零长度数组时,即使长度精确,也要先 new 出该数组,再逐个赋值,最后还可能因泛型擦除做
instanceof校验 - JDK 11+ 进一步优化了
new T[0]分支,对小列表(Unsafe.allocateInstance
toArray(new String[0]) 在 ArrayList 和 CopyOnWriteArrayList 中表现不同
这是容易被忽略的关键差异:ArrayList 的 toArray 对 new T[0] 有深度优化;但 CopyOnWriteArrayList 没有——它的实现始终先 new Object[size],再批量 copy,最后用 Arrays.copyOf 转型。所以传 new String[0] 对它没收益,反而多一次转型开销。
- ArrayList:推荐无条件用
list.toArray(new String[0]) - CopyOnWriteArrayList:用
list.toArray(new String[list.size()])更稳,尤其在高并发读场景下可省去一次扩容拷贝 - 自定义 List 实现?别假设行为一致,务必看源码里
toArray(T[])是否重写了 fast-path
泛型擦除导致的 ClassCastException 风险点
错误不在 toArray 调用本身,而在后续强转或误用返回数组类型。比如 List<Object> 存了 Integer,却用 list.toArray(new String[0]) ——这不会在 toArray 时报错,但返回的其实是 String[],里面每个元素都是 null(因为转型失败后填默认值),运行时取值才 NPE 或 ClassCastException。
- 真正安全的前提是:List 元素类型与目标数组组件类型兼容(即能 assignment-compatible)
- 编译器不检查运行时类型,所以
(String) list.get(0)成功 ≠list.toArray(new String[0])安全 - 如果不确定元素类型,宁可用
list.toArray()得到Object[],再手动遍历转型
Android 和旧 JDK(
Android(Dalvik/ART)早期版本和 JDK 7 及更早,对 new T[0] 的优化不完整。部分 ROM 上 toArray(new String[0]) 反而比预分配慢 10%~20%,因为反射调用开销未被 JIT 充分消除。
立即学习“Java免费学习笔记(深入)”;
- JDK 6:建议统一用
new String[list.size()],避免不可预测的反射延迟 - Android API 21(Lollipop)以下:同上,尤其低端设备上
new String[0]触发的Class.forName可能卡顿 - 只要项目最低支持 JDK 8 / Android API 24+,就放心用
new String[0],现代 JIT 已把它当作常量路径处理









