你是不是也这样过?写代码,一上来就是:
java
List<String> list = new ArrayList<>();
管它三七二十一,先new
一个ArrayList
再说!
我以前也是这样。直到有一次,线上接口慢得像蜗牛,排查半天,发现是某个地方用了ArrayList
存了几万条数据,还频繁删除插入......直接卡崩了。
容器用错了,性能能差十倍不止。
下面记录一下。
1. ArrayList:适合读多写少
你从数据库拉了一堆用户列表,展示用,基本不改。
这种情况,ArrayList
是首选。
java
List<User> users = userService.getAllUsers();
for (User u : users) {
render(u); // 大部分是读操作
}
// 后面全是遍历展示,不增不删
它的优点是按位置访问快。你要是在中间insert
或remove
,它就得把后面的元素全往后挪,特别耗时。
所以,要是频繁插入删除的操作,别用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 官方都推荐用它替代Stack
和LinkedList
做栈或队列。
除非你要中间插入,否则优先选它。
总结一下
需求 | 推荐用 |
---|---|
普通列表,读多写少 | 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个真实场景让你代码更优雅》