如何快速初始化包含指定数量相同对象的ArrayList且无额外拷贝?
解决方案
以下方案均满足返回ArrayList类型、支持任意泛型、与原有实现逻辑完全一致(所有位置存储同一个对象引用)的要求。
方案一:反射实现(性能最优,零额外数组拷贝)
该方案直接替换ArrayList内部的数组字段,避免构造方法的数组拷贝开销,大size场景下性能最高。
import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; public class ArrayListInitUtil { private static final Field ELEMENT_DATA_FIELD; private static final Field SIZE_FIELD; // 提前缓存反射字段,避免每次调用反射开销 static { try { ELEMENT_DATA_FIELD = ArrayList.class.getDeclaredField("elementData"); ELEMENT_DATA_FIELD.setAccessible(true); SIZE_FIELD = ArrayList.class.getDeclaredField("size"); SIZE_FIELD.setAccessible(true); } catch (NoSuchFieldException e) { throw new RuntimeException("Failed to access ArrayList internal fields", e); } } public static <T> ArrayList<T> initArrayList(int size, T element) { try { ArrayList<T> list = new ArrayList<>(0); Object[] arr = new Object[size]; Arrays.fill(arr, element); ELEMENT_DATA_FIELD.set(list, arr); SIZE_FIELD.set(list, size); return list; } catch (IllegalAccessException e) { throw new RuntimeException("Failed to init ArrayList", e); } } }
注意事项
- 静态代码块提前缓存反射字段,单次调用的反射固定开销可忽略不计,size>100场景下性能远超手写循环和数组拷贝方案。
- Java 9+开启模块系统时,需要添加JVM启动参数
--add-opens java.base/java.util=ALL-UNNAMED允许反射访问ArrayList私有字段。
方案二:全版本兼容无反射方案
如果不能使用反射,优化后的手写循环方案性能也优于Arrays.fill + 构造拷贝实现:
import java.util.ArrayList; public static <T> ArrayList<T> initArrayList(int size, T element) { ArrayList<T> list = new ArrayList<>(size); for (int i = 0; i < size; i++) { list.add(element); } return list; }
性能说明
ArrayList预分配容量后,add方法仅做数组赋值和size自增,无需扩容判断,性能仅略低于反射方案,且兼容所有JDK版本。
注:所有实现均保持和原有逻辑一致,数组中存储的是同一个对象的引用,若使用可变对象,修改任意位置的对象会影响所有元素。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

