java arraylist需按场景选方法:读多写少用get/set和add(e),避免中间增删;批量操作预估容量防扩容;自定义对象须重写equals/hashcode;多线程需外部同步或换并发容器。

Java ArrayList 在项目中不是“能用就行”,而是要结合数据访问模式、增删频率和线程环境来选方法、避坑点。用对了,性能稳;用错了,小项目不显,大流量一压就慢。
按场景选方法:读多写少就用 get/set,尾部追加优先 add(E)
大多数业务列表(如订单列表、用户配置项、日志缓存)都是“查得多、改得少、新增基本在末尾”。这时:
- get(int index) 是首选——O(1)随机访问,遍历、分页、取头尾元素都靠它;
- set(int index, E element) 替换值快且安全,不会触发移动或扩容;
- add(E e) 往末尾加元素最轻量,只在容量不足时才扩容,日常批量导入、日志收集都走这条路;
- 避免高频调用 add(int index, E) 或 remove(int index) 插入/删除中间位置——比如在循环里反复往 list 开头 add,数据量超千条后性能断崖式下跌。
批量操作要预估容量,别让扩容拖垮吞吐
ArrayList 默认容量是 10,第一次 add 才真正分配数组;后续扩容按 1.5 倍增长(10→15→22→33…),每次扩容都要复制全部元素。项目中常见反模式:
- 新建空 ArrayList,然后 for 循环 add 5000 条数据 → 触发约 7 次扩容,产生大量临时数组和内存拷贝;
- 修复很简单:用 new ArrayList(预计数量) 或提前调用 ensureCapacity(5000);
- 如果数据来自数据库查询结果集,可用 new ArrayList(resultList.size()) 初始化,零冗余扩容。
删除和查找要注意语义差异与重写要求
ArrayList 的 remove 和 contains 行为容易误用:
-
remove(Object o) 调用的是元素的
equals()方法,不是 ==;自定义对象(如 User、Order)必须重写equals和hashCode,否则永远删不掉; - contains(Object o) 同理,依赖 equals 判断是否存在,没重写就只能匹配引用相等;
- 需要按条件删多个元素?别用 for + remove(i),会跳过下一个元素;改用迭代器
iterator.remove(),或收集索引后倒序删除。
线程安全不能靠 ArrayList 自身,得靠外部控制
ArrayList 不是线程安全的——多线程同时 add 或 clear,可能抛 ConcurrentModificationException,甚至出现数据丢失或数组越界。项目中常见做法:
- 单线程场景(如 Spring MVC 的 Controller 方法内)放心用,无需包装;
- 多线程共享且有写操作?用 Collections.synchronizedList(new ArrayList()),但注意:它只保证单个方法原子,复合操作(如 if-not-exists-then-add)仍需手动同步;
- 高并发读多写少?考虑 CopyOnWriteArrayList,适合监听器列表、配置快照等场景;
- 真要高性能并发写?通常该换数据结构——比如用 ConcurrentHashMap 分片管理,而不是硬扛 ArrayList。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











