空数组性能更优,因jdk 11+对new t[0]有专用优化路径,直接调用arrays.copyof避免预分配开销;而预分配需先创建数组再拷贝,gc压力大且易出错。
![java中 collection 接口的 toarray(t[] a) 传入 0 长度数组与预配长度数组的性能对比](https://img.php.cn/upload/article/001/242/473/178476130846408.jpeg?x-oss-process=image/resize,p_40)
传入 0 长度数组(如 new String[0])通常比预分配正确长度的数组(如 new String[list.size()])性能更好,尤其在 JDK 11+ 中。核心原因不是“避免扩容”,而是 JVM 和集合实现对空数组有专门优化路径。
为什么空数组反而更快?
现代 JDK(尤其是 11 及以后)中,ArrayList.toArray(T[]) 等主流实现会检测传入数组是否为空(a.length == 0),若是,则直接调用 Arrays.copyOf(elementData, size, a.getClass())。这个方法底层由 JVM 内联为高效的内存拷贝指令(如 System.arraycopy 或更底层的汇编优化),且无需预先创建目标数组对象。
而预分配数组需两步:先执行 new T[size](触发堆内存分配和零初始化),再拷贝元素。当 size 很大时,分配开销显著;即使 JVM 有逃逸分析和标量替换,也并非总能完全消除。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
实际性能差异的关键点
-
JDK 版本影响大:JDK 8 对空数组无特殊处理,可能略慢于预分配;JDK 11+ 引入了针对
new T[0]的专用 fast-path,优势明显 -
数组类型有影响:泛型擦除后实际是
Object[]拷贝,但若传入String[]、Integer[]等具体类型,JVM 能更好内联和优化;用Object[]反而可能失去类型特化优势 - 集合大小决定收益:小集合(如 size 1000)下空数组方式 GC 压力更小、分配次数更少
推荐写法与注意事项
直接使用 list.toArray(new Type[0]) 是当前最佳实践,简洁且高效:
String[] arr = list.toArray(new String[0]); // ✅ 推荐,JDK 11+ 最优
不建议手动预估长度或缓存数组(易出错且无必要);也不要写 list.toArray(new Type[list.size()]) —— 这在多数场景下更慢,且当集合为空时还可能因类型擦除导致 ClassCastException(如 list 实际是 ArrayList<object></object> 却传入 String[])。
特殊情况需留意
- Android(Dalvik/ART):早期版本对空数组优化不足,预分配可能更稳;新版本已逐步对齐 OpenJDK 行为
-
自定义 Collection 实现:若未重写
toArray(T[])或未做空数组优化,行为取决于父类(如AbstractCollection默认实现仍会新建数组) - 需要复用数组场景:极少数情况(如高频循环中固定大小缓冲区),预分配并复用数组可减少 GC,但应配合对象池或 ThreadLocal,而非简单每次 new
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










