应优先用 Set 替代 List 存储唯一值,因其语义明确、去重高效(O(1));频繁按字段查找时应用 Map 预构建索引;并发场景避免直接使用 ArrayList/LinkedList,改用线程安全集合;固定小集合宜用 enum 或 Set.of() 保障类型安全与不可变性。

用 List 存储唯一值时就该换 Set
Java 的 List 允许重复、保留插入顺序,但如果你在代码里反复调用 list.contains(x) 去查重,或者手动遍历去 dedupe,性能会随数据量线性变差。更关键的是语义错位——List 本就不承诺“不重复”,后续维护者可能随意 add() 两次相同对象而不自知。
此时应直接用 HashSet(不要求顺序)或 LinkedHashSet(要顺序且去重)。contains() 平均时间复杂度从 O(n) 降到 O(1),逻辑也更自解释。
需要按字段快速查找时别硬塞 List + for 循环
比如有一组 User 对象,经常要根据 userId 取单个实例。写成 list.stream().filter(u -> u.getId() == id).findFirst() 看似简洁,但每次都是 O(n) 扫描;若查询频繁,实际已退化为数据库没建索引的状态。
正确做法是预构建 Map,用 userId 当 key:
Map后续userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
userMap.get(id) 是 O(1),且意图清晰。别用 List 扛本该由哈希结构解决的查找问题。
并发修改场景下直接用 ArrayList 或 LinkedList 会出 ConcurrentModificationException
只要多个线程可能同时读写同一个 List,哪怕只用 iterator() 遍历 + 另一个线程调 add(),就大概率触发异常。这不是“偶尔出错”,而是迭代器检测到结构性修改后的主动失败。
可选方案有:
- 用
Collections.synchronizedList(new ArrayList())—— 但所有操作都串行化,吞吐低 - 改用
CopyOnWriteArrayList—— 适合读多写少,写操作复制整个数组,内存和 GC 压力大 - 更推荐:用
ConcurrentHashMap替代,把“列表索引”转为“键值对”,尤其当你要随机访问或范围查询时
List 成为并发瓶颈的默认选择。
存储配置项、枚举态等固定小集合时,List 比 enum 或 Set.of() 更难维护
比如权限列表 ["READ", "WRITE", "DELETE"],如果定义成 static final List,就失去类型安全、不可变性弱、无法在 switch 中使用,还容易被外部误修改(哪怕加了 unmodifiableList,也挡不住字符串内容本身被篡改)。
更稳妥的方式:
- 用
enum Permission { READ, WRITE, DELETE }—— 编译期校验、可序列化、支持方法扩展 - 若必须用字符串集合,用
Set.of("READ", "WRITE", "DELETE")(Java 9+),不可变、轻量、语义明确
List 在这里只是容器,不是设计选择——它没提供任何针对“固定集合”的约束能力。立即学习“Java免费学习笔记(深入)”;
集合类型不是越通用越好。List 的灵活性背后是责任:你要自己保证顺序、重复、并发、查找效率这些事。一旦其中某条约束开始失控,就是重构信号。










