别再乱 new ArrayList!8 大 Java 容器选型案例,一篇看懂

你是不是也这样过?写代码,一上来就是:

java 复制代码
List<String> list = new ArrayList<>();

管它三七二十一,先new一个ArrayList再说!

我以前也是这样。直到有一次,线上接口慢得像蜗牛,排查半天,发现是某个地方用了ArrayList存了几万条数据,还频繁删除插入......直接卡崩了。

容器用错了,性能能差十倍不止。

下面记录一下。

1. ArrayList:适合读多写少

你从数据库拉了一堆用户列表,展示用,基本不改。

这种情况,ArrayList是首选。

java 复制代码
List<User> users = userService.getAllUsers();
for (User u : users) {
    render(u); // 大部分是读操作
}
// 后面全是遍历展示,不增不删

它的优点是按位置访问快。你要是在中间insertremove ,它就得把后面的元素全往后挪,特别耗时。

所以,要是频繁插入删除的操作,别用ArrayList

2. LinkedList:适合链式操作的场景

有次我们做后台管理,要支持"撤销上一步"功能。

比如用户连续改了 5 次标题,点撤销,就得一步步退回去。

最开始用ArrayList存操作记录:

java 复制代码
List<EditAction> history = new ArrayList<>();

每次撤销,都要remove最后一条。结果数据一多,删除慢得不行,界面卡顿。

后来换成LinkedList

java 复制代码
LinkedList<EditAction> history = new LinkedList<>();

因为是链表结构,删最后一个节点,O(1) 就搞定。

而且我们还用它当栈使用:

scss 复制代码
history.addLast(action);  // 入栈
history.removeLast();     // 出栈,撤销

LinkedList的头尾操作天生就快,指针一指,完事。

3. HashMap:日常键值存储

用户登录后,把基本信息缓存在内存里。

java 复制代码
Map<Long, User> userMap = new HashMap<>();
userMap.put(user.getId(), user);
User target = userMap.get(10086L);

查得快,写得也快,日常开发用得最多。

但注意,它不保证顺序,也不支持并发。

4. LinkedHashMap:要顺序,就它了

有时候你希望Map的遍历顺序和插入顺序一致。

比如记录用户操作日志,按时间顺序存。

java 复制代码
Map<String, Action> log = new LinkedHashMap<>();
log.put("login", loginAction);
log.put("pay", payAction);
// 遍历时,先 login,后 pay

遍历时,最近访问的在前面。

而且插入顺序固定,前端展示不乱。

5. TreeMap:需要自动排序

你要按分数给学生排名,key是分数。

TreeMap,它自动按key排好。

java 复制代码
Map<Integer, String> rank = new TreeMap<>();
rank.put(95, "张三");
rank.put(88, "李四");
// 遍历出来就是按分数从小到大

遍历出来就是有序的。

虽然比HashMap慢点,但省了你手动排序的麻烦。


6. ConcurrentHashMap:并发写 Map,就它了

多个线程同时往缓存里put数据。

java 复制代码
Map<String, Object> cache = new ConcurrentHashMap<>();
cache.put("token_123", token);
Object obj = cache.get("token_123");

线程安全,性能好,高并发场景下Map的唯一选择。

别用Collections.synchronizedMap,太慢。

7. CopyOnWriteArrayList:读多写少的并发 List

系统里有一堆短信发送监听器。

大部分时间都在遍历通知:

java 复制代码
List<EventListener> listeners = new CopyOnWriteArrayList<>();
for (EventListener l : listeners) {
    l.onSend(sms);
}
// 遍历通知时完全无锁,特别快
// 写操作会复制数组,所以写不能太频繁

它的读操作不加锁,性能极高。

但每次写都要复制整个数组,写多的话,内存和 CPU 都扛不住。

所以,写得少,读得多,才考虑它。

8. ArrayDeque:队列和栈,首选它

实现一个任务队列,或者当栈用。

java 复制代码
Deque<Task> tasks = new ArrayDeque<>();
tasks.addLast(newTask); // 入队
Task t = tasks.removeFirst(); // 出队

它底层是数组,比LinkedList内存更加紧凑,操作更快。

Java 官方都推荐用它替代StackLinkedList做栈或队列。

除非你要中间插入,否则优先选它。

总结一下

需求 推荐用
普通列表,读多写少 ArrayList
头尾增删频繁 ArrayDeque
键值对,快 HashMap
要保持插入顺序 LinkedHashMap
key 需要排序 TreeMap
多线程写 Map ConcurrentHashMap
多线程读多写少 List CopyOnWriteArrayList
队列或栈 ArrayDeque

容器不是随便选的。

用对了,程序跑得稳;用错了,半夜叫你修 bug。

我是大华,关注我,少走弯路,一起进步! 觉得有用的话,点个爱心,让更多人看到吧!

📌往期精彩

《Elasticsearch 太重?来看看这个轻量级的替代品 Manticore Search》 《别再if套if了!Java中return的9种优雅写法》 《别学23种了!Java项目中最常用的6个设计模式,附案例》 《Vue3+TS设计模式:5个真实场景让你代码更优雅》

相关推荐
沧澜sincerely24 分钟前
Redis 键空间 & 五大类型
java·redis·docker
2351626 分钟前
【LeetCode】3. 无重复字符的最长子串
java·后端·算法·leetcode·职场和发展
nandao15828 分钟前
springBoot 集成Neo4j 实战演示
java·spring boot·neo4j
可观测性用观测云41 分钟前
解锁DQL高级玩法——对日志关键信息提取和分析
后端
零雲43 分钟前
java面试:可以讲一讲sychronized和ReentrantLock的异同点吗
java·开发语言·面试
没有bug.的程序员1 小时前
SQL 执行计划解析:从 EXPLAIN 到性能优化的完整指南
java·数据库·sql·性能优化·explain·执行计划
微笑尅乐1 小时前
神奇的位运算——力扣136.只出现一次的数字
java·算法·leetcode·职场和发展
Chan161 小时前
【 设计模式 | 结构型模式 代理模式 】
java·spring boot·后端·设计模式·intellij-idea
柯南二号1 小时前
【AI】【Java后端】RAG 实战示例:SpringBoot + 向量检索 + LLM 问答系统
java·人工智能·spring boot
Mr.Pascal1 小时前
后端直接返回错误信息的Map 和 抛出异常(异常机制)优劣势对比
java·springboot