Java 集合框架:接口体系、常用实现、底层结构与选型
0. 阅读指南
-
日常开发最常用接口:
List、Map、Set(其次是Queue/Deque)。 -
List<E>:List是接口 ,E是泛型元素类型 ;ArrayList是List的实现类。 -
"集合是否安全"通常指线程安全 :接口不承诺线程安全,取决于实现类 + 是否被多线程共享。
-
选型口诀:
默认:ArrayList + HashMap + HashSet;要顺序:LinkedHash*;要排序/范围:Tree*;并发共享:ConcurrentHashMap;栈/队列:ArrayDeque。
1. 什么是集合框架(Collection Framework)?
集合框架可以理解为:JDK 提供的一套"标准化的数据容器体系",用于存放和操作数据,让我们把精力放在业务逻辑上,而不是重复造轮子。
一个完整的集合框架通常包含三部分:
-
接口(Interfaces)
定义抽象数据类型(如
List/Set/Map),让我们在代码里"面向接口编程",不关心具体实现。 -
实现(Implementations)
接口的具体实现类(如
ArrayList/HashMap),内部使用不同数据结构以达到不同性能特点。 -
算法(Algorithms)
对集合进行通用操作的方法(如排序、查找、批处理),常见于
Collections、Arrays等工具类。
2. 集合体系结构:Collection vs Map
Java 容器主要分两大类:
2.1 Collection:单列集合(存一个个元素)
-
Collection<E>-
List<E>:有序、可重复 -
Set<E>:唯一(去重) -
Queue<E>/Deque<E>:队列/双端队列(工程中也很常用)
-
2.2 Map:键值对集合(key -> value)
-
Map<K,V>不继承Collection这是很多初学者容易混的点:
Map自成体系。
3.日常 Java 开发常用接口
1) List 体系:最常用
-
List<E>:最常用的集合接口之一,有序、可重复,绝大多数"结果集/展示列表/批量处理"都是 List。 -
(常一起用的通用能力)
Collection<E>/Iterable<E>:很多方法参数会写成它们以提高通用性,但你写业务代码时还是以List为主。
2) Set 体系:常用,但场景更明确
-
Set<E>:核心就是去重(元素唯一),常用于"判重/过滤重复/黑白名单/去重统计"等。 -
SortedSet<E>/NavigableSet<E>:需要排序 + 范围查找 时会用(比如按时间/ID 有序、取区间、取最近值),但比Set少很多。
3) Map 体系:和 List 并列最常用
-
Map<K,V>:几乎所有项目都会大量用到,键值映射/快速查找/配置参数/聚合统计/分组结果等都离不开它。 -
ConcurrentMap<K,V>:并发场景(缓存、计数器、共享状态)常用,尤其在多线程/高并发服务里。 -
SortedMap<K,V>/NavigableMap<K,V>:需要按 key 有序 + 范围操作 时用(如时间线、区间查询),频率低于Map。
List<E> 是什么?和 ArrayList 有什么关系?
1 先一句话说清楚
-
List是"规则/标准"(接口) -
ArrayList是"按这个规则做出来的具体东西"(实现类) -
E是"里面装什么类型的元素"(泛型)
2 用生活类比理解(最直观)
把 List 当成"插座标准 ",它规定了你必须支持哪些功能:
比如能 add()、能 get()、能 remove()......
ArrayList 就像"某个品牌按这个标准生产出来的插座 "。
同样符合 List 标准的,还有别的品牌/型号,比如 LinkedList。
所以:
-
List:规定能力(接口) -
ArrayList:具体实现(实现类)
3 E 到底是什么?
E 是泛型参数,意思是 Element(元素)。
举例:
-
List<String>:只能装字符串 -
List<Integer>:只能装整数(实际上装的是Integer,基本类型会装箱) -
List<User>:只能装 User 对象
4 它们之间的关系(最关键的一句)
ArrayList 实现了 List:
java
ArrayList<E> implements List<E>
这句的意思就是:
ArrayList 具备 List 规定的一切功能。
5 为什么代码里常写 List = new ArrayList()?
你经常看到这种写法:
java
List<String> list = new ArrayList<>();
这叫面向接口编程 ,好处是:
以后如果你发现 ArrayList 不合适,想换成别的实现,改动很小:
java
List<String> list = new LinkedList<>();
业务代码只认 List 的规则(add/get/remove),不关心底层用的是哪种实现。
6 一句话总结
List<E>是一个"列表接口",规定了列表必须有哪些操作;ArrayList是List的一个实现类;E表示列表里元素的类型。
4. List / Set / Map 的区别
4.1 List
-
有序(有索引)
-
可重复
-
多数实现允许
null -
常见场景:结果集、分页列表、批量处理、展示数据
4.2 Set
-
元素唯一(去重)
-
"无序"更准确说法:迭代顺序不保证(由具体实现决定)
-
HashSet/LinkedHashSet通常允许一个null -
常见场景:判重、去重、黑白名单、集合运算(交并差)
4.3 Map
-
key -> value映射 -
key必须唯一,value可重复 -
HashMap允许 1 个nullkey;ConcurrentHashMap/Hashtable不允许null -
常见场景:按 id 查对象、分组统计、缓存、参数映射
5. 常用集合类有哪些?(开发最常见)
5.1 List 常用
-
ArrayList(最常用) -
LinkedList(更多时候当Deque用) -
Vector/Stack(老类,通常不推荐新项目使用)
5.2 Set 常用
-
HashSet -
LinkedHashSet(保持插入顺序) -
TreeSet(有序、支持范围查询)
5.3 Map 常用
-
HashMap(最常用) -
LinkedHashMap(保持顺序 / 可实现 LRU) -
TreeMap(按 key 排序 / 范围查询) -
ConcurrentHashMap(并发场景)
6. 选型表:常用类 + 底层结构 + 复杂度
这张表就是"你该用哪个集合"的速查表。
| 典型场景 | 推荐实现类 | 接口 | 底层数据结构 | 典型复杂度 | 线程安全 | 备注 |
|---|---|---|---|---|---|---|
| 普通列表/结果集,随机访问多 | ArrayList |
List |
动态数组 Object[] |
get O(1),尾插均摊 O(1),中间改 O(n) |
否 | 最常用 List |
| 头尾频繁增删(队列/栈) | ArrayDeque |
Deque |
循环数组 | 头尾操作均摊 O(1) | 否 | 推荐替代 Stack ;不允许 null |
| 按优先级取最小/最大 | PriorityQueue |
Queue |
堆 | offer/poll O(log n),peek O(1) |
否 | 不保证遍历有序 |
| 判重/去重 | HashSet |
Set |
基于 HashMap |
平均 O(1) | 否 | 依赖 hashCode/equals |
| 去重 + 保持插入顺序 | LinkedHashSet |
Set |
LinkedHashMap(Hash + 双向链表) |
平均 O(1) | 否 | 遍历顺序=插入顺序 |
| 排序/范围查询(<=、区间) | TreeSet |
NavigableSet |
红黑树(TreeMap) |
O(log n) | 否 | 通常不接受 null(取决于比较器) |
| key 查 value / 分组统计 / 缓存 | HashMap |
Map |
数组 + 链表/红黑树(JDK8+) | 平均 O(1) | 否 | 允许 1 个 null key |
| Map 保持插入顺序 / LRU | LinkedHashMap |
Map |
HashMap + 双向链表 |
平均 O(1) | 否 | 可实现 LRU |
| key 有序 + 范围查询 | TreeMap |
NavigableMap |
红黑树 | O(log n) | 否 | 适合区间/最值 |
| 多线程共享 Map | ConcurrentHashMap |
ConcurrentMap |
并发哈希结构 | 并发下接近 O(1) | 是 | 不允许 null;用原子 API |
| 读多写少并发 List/Set | CopyOnWriteArrayList/Set |
List/Set |
写时复制数组 | 读 O(1),写 O(n) | 是 | 配置/白名单很适合 |
| 简单粗暴同步包装 | Collections.synchronizedXxx |
多种 | 外层锁包装 | 取决于内部实现 | 是 | 遍历仍需手动同步 |
7. 集合"线程安全"到底怎么理解?(Map vs ConcurrentMap)
先给结论:接口不保证线程安全,看实现类 + 使用方式。
7.1 常见非线程安全(默认)
ArrayList、HashMap、HashSet、LinkedHashMap、TreeMap...
如果它们只是方法内局部变量(每次请求新建、不共享),一般没问题。
7.2 常见线程安全 / 并发友好
-
ConcurrentHashMap(最常用并发 Map) -
CopyOnWriteArrayList/Set(读多写少) -
Collections.synchronizedMap/List/Set(...)(同步包装) -
Vector/Hashtable/Stack(老的同步类,新项目一般不推荐)
7.3 并发容器也要"用对姿势"
错误写法(复合操作有竞态):
java
if (!map.containsKey(k)) {
map.put(k, v);
}
正确写法(原子操作):
java
map.putIfAbsent(k, v);
// 或
map.computeIfAbsent(k, kk -> createValue());
8. 数组 vs 集合
-
数组:长度固定;集合:通常可动态扩容
-
数组可存基本类型(
int[])和引用类型(String[])集合只能存引用类型,但基本类型会自动装箱 (
List<Integer>) -
"数组必须同类型、集合可不同类型"这句容易误导:
如果集合使用泛型(推荐),同样会限制类型;不使用泛型才可能混放,但不推荐。
9. 面试/实战速答模板
-
List/Set/Map 区别:List 有序可重复;Set 去重唯一;Map 键值映射 key 唯一。
-
ArrayList vs LinkedList:ArrayList 随机访问快;LinkedList 头尾增删更方便(常当 Deque)。
-
HashMap vs TreeMap:HashMap 无序但快;TreeMap 有序支持范围查询(O(log n))。
-
HashMap vs ConcurrentHashMap:前者非线程安全;后者并发友好且提供原子复合操作(但不允许 null)。
10. 总结
-
最常用接口:
List、Map、Set -
最常用实现:
ArrayList、HashMap、HashSet -
要顺序:
LinkedHashMap/LinkedHashSet;要排序/范围:TreeMap/TreeSet -
多线程共享:优先
ConcurrentHashMap,并用computeIfAbsent/putIfAbsent这类原子方法