ArrayList是最接近动态数组的标准实现,底层用可扩容数组存储,支持add()等操作;原生数组长度固定,无法动态增删;扩容时复制数据导致O(n)开销,需预设容量优化性能;遍历时修改须用Iterator.remove()或removeIf()避免ConcurrentModificationException。

Java 里没有真正意义上的“动态数组”,ArrayList 是最接近它的标准实现——它底层用数组存储,但封装了自动扩容逻辑,用起来像可变长数组。
为什么不能直接 new int[] 然后 add()?
原生数组(如 int[]、String[])长度固定,声明后无法增删元素。ArrayList 是对象容器,只能存引用类型(或装箱后的基本类型),所以 ArrayList 是非法的,必须写成 ArrayList。
-
int[] arr = new int[3];→ 长度永远是 3,arr.add(4)编译不通过 -
ArrayList→ 可以反复list = new ArrayList(); add(),内部自动扩容 - 如果真要操作基本类型数组并动态调整,得自己封装或用第三方库(如 Apache Commons Lang 的
ArrayUtils)
add() 和 ensureCapacity() 怎么影响性能?
add() 在尾部插入时平均时间复杂度是 O(1),但触发扩容时是 O(n) —— 因为要新建数组、复制旧数据。默认初始容量是 10,每次扩容约 1.5 倍(JDK 14+ 是 oldCapacity + (oldCapacity >> 1))。
- 如果已知大概要存 1000 个元素,初始化时写
new ArrayList(1024)能避免多次扩容 -
ensureCapacity(2000)可以提前预留空间,但不会缩容;trimToSize()才能收缩到当前元素数 - 频繁在头部或中间
add(index, e)会导致大量元素位移,此时应考虑LinkedList(但注意:随机访问会变慢)
遍历时修改集合抛 ConcurrentModificationException 怎么办?
用普通 for 循环或增强 for(for (E e : list))遍历时调 list.remove(),会触发 fail-fast 机制,直接抛 ConcurrentModificationException。
立即学习“Java免费学习笔记(深入)”;
- 安全删除要用
Iterator.remove():Iterator
it = list.iterator(); while (it.hasNext()) { if (it.next().length() == 0) it.remove(); } - Java 8+ 推荐用
removeIf():list.removeIf(s -> s.isEmpty()); - 想边遍历边添加,得转成数组或先收集待操作索引,再批量处理——
ArrayList不支持并发修改
真正容易被忽略的是:扩容策略和迭代器的强一致性检查都是隐藏行为,不报错时一切正常,一旦数据量上来或混合多线程操作,问题才暴露。别只盯着“能加能删”,得看它在哪悄悄复制数组、在哪突然中断循环。









