Java集合框架围绕Collection和Map主线,解决存储、访问、重复性、空值及线程安全问题;选错类易致性能下降或ConcurrentModificationException等异常。

Java集合框架不是“学完就忘”的概念堆砌,而是围绕Collection和Map两条主线,解决“存什么、怎么存、怎么取、是否允许重复/空值、线程是否安全”这些具体问题的工具箱。选错集合类,轻则性能掉一截,重则出现ConcurrentModificationException或NullPointerException。
ArrayList vs LinkedList:别只看“数组”和“链表”字面意思
很多人以为“查多用ArrayList,增删多用LinkedList”,但实际要拆开看操作位置:
-
ArrayList.get(int index)是O(1),但remove(int index)在中间位置是O(n)——因为要数组拷贝移动 -
LinkedList.get(int index)是O(n),哪怕查第10个元素也要从头/尾遍历;它真正快的是addFirst()、addLast()、removeFirst()这些Deque接口方法 - 除非你明确在头部频繁插入/删除(比如实现栈、队列),否则
LinkedList在JVM中因对象头开销大、缓存不友好,实际性能往往不如ArrayList
Listlist = new ArrayList<>(); list.add("a"); list.add("b"); // ✅ 推荐:随机访问或尾部追加 String s = list.get(0); List queue = new LinkedList<>(); queue.add("first"); queue.add("second"); // ✅ 推荐:当作队列用 String head = queue.removeFirst(); // O(1)
HashMap的key为什么必须重写hashCode()和equals()
不重写会导致两个逻辑相等的对象被当成不同key存入,或者查不到已存在的key——这是最常被忽略的坑。
-
HashMap先用key.hashCode()定位桶(bucket),再用key.equals()比对链表/红黑树中的节点 - 如果只重写
equals()不重写hashCode(),相同对象可能落在不同桶里,get()直接返回null - 如果只重写
hashCode()不重写equals(),不同对象可能哈希冲突后无法正确判断是否相等,导致重复key
public class User {
private String name;
private int age;
// 必须同时生成!IDE通常可一键生成
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
}
ConcurrentHashMap不是“线程安全版HashMap”,它的并发策略变了
Java 8之后的ConcurrentHashMap不再用分段锁(Segment),而是基于synchronized + CAS + 红黑树迁移实现,但行为差异很关键:
Sylius开源电子商务平台是一个开源的 PHP 电子商务网站框架,基于 Symfony 和 Doctrine 构建,为用户量身定制解决方案。可管理任意复杂的产品和分类,每个产品可以设置不同的税率,支持多种配送方法,集成 Omnipay 在线支付。功能特点:前后端分离Sylius 带有一个强大的 REST API,可以自定义并与您选择的前端或您的微服务架构很好地配合使用。如果您是 Symfony
立即学习“Java免费学习笔记(深入)”;
-
size()不是强一致的——它返回的是估算值,高并发下可能滞后;需要精确计数请用mappingCount() -
containsKey()和get()是弱一致性读,不会阻塞写操作,但可能读到“刚刚被删除的key”的旧值(极短窗口) - 不支持
Iterator的remove(),调用会抛UnsupportedOperationException;要用computeIfPresent()或remove(key, value)
ConcurrentHashMapmap = new ConcurrentHashMap<>(); map.put("a", 1); map.computeIfPresent("a", (k, v) -> v + 1); // ✅ 安全更新 // map.keySet().iterator().next().remove(); // ❌ 抛异常
集合类的选择不是靠记忆API,而是盯住你的数据访问模式:是否需要排序?是否要保证插入顺序?是否有多线程写入?有没有null键需求?这些约束条件一旦明确,候选集合通常只剩一两个。最危险的,是把HashMap直接扔进多线程环境还坚信“只是读多写少就没事”。









