Java 常用数据结构:API + 实现类型 + 核心原理 + 例子 + 选型与性能(完整版)
目标:把"怎么用(API)"和"为什么快/慢(实现原理)"放在同一份文档里。
约定:
- 复杂度以 JDK 常见实现为准(均摊/平均),极端情况会更差。
- 示例默认
import java.util.*;(并发类额外写全名)。- 推荐:左边接口,右边实现 (例如
Deque<Integer> dq = new ArrayDeque<>();)。
0) Java 集合框架总览(正确关系)
Collection:List/Set/QueueDeque:Queue的子接口(双端队列,可当栈)Map:独立体系,不属于Collection

1) 数组 Array(T[] / int[])
实现类型与原理
- JVM 原生连续内存(逻辑上连续),通过下标偏移寻址
- 随机访问直接 O(1),插删需要整体搬移
常用 API/语法
a.lengtha[i]读写Arrays.sort(a)Arrays.binarySearch(a, key)Arrays.copyOf(a, n)System.arraycopy(...)
示例
java
int[] a = {3, 1, 2};
Arrays.sort(a); // [1,2,3]
int idx = Arrays.binarySearch(a, 2); // 1
什么时候用
- 原地算法、性能敏感、长度固定或可预估(计数、双指针、前缀和)
性能
- 访问 O(1)
- 中间插删 O(n)
- 内存开销最低
2) List(有序、可重复、可按下标)
2.1 ArrayList(动态数组,最常用)
实现类型与原理
- 底层:
Object[] elementData - 扩容:容量不足时创建新数组 +
System.arraycopy拷贝 - JDK 常用扩容策略:按比例增长(大致 1.5 倍,细节与版本有关)
- 随机访问快;中间插删慢(搬移)
常用 API
add(e),add(i,e)get(i),set(i,e)remove(i),remove(obj)size(),isEmpty(),clear()contains,indexOfsubList(l,r)(视图,修改会影响原 list)
示例
java
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(1, 15); // [10,15,20]
int x = list.get(2); // 20
list.remove(Integer.valueOf(15));
什么时候用
- 频繁
get/set、遍历、尾部追加 - LeetCode 默认 list 选它
性能
get/set:O(1)- 尾插:均摊 O(1)
- 中间插删:O(n)
- 额外:扩容会有一次性 O(n) 拷贝
2.2 LinkedList(双向链表,实现 List + Deque)
实现类型与原理
- 底层:双向链表节点
Node{E item; Node next; Node prev} - 头尾操作 O(1)
- 随机访问 O(n)(要从头/尾走链)
- 内存开销大(节点对象 + prev/next)
常用 API(Deque 语义更常用)
addFirst/addLast,offerFirst/offerLastpollFirst/pollLast,removeFirst/removeLastpeekFirst/peekLast
示例
java
LinkedList<Integer> ll = new LinkedList<>();
ll.addLast(1);
ll.addLast(2);
ll.addFirst(0); // 0 1 2
int a = ll.pollFirst(); // 0
什么时候用
- 需要频繁头尾增删且数据量不大
- 但栈/队列更推荐
ArrayDeque
性能
- 头尾增删:O(1)
get(i):O(n)- 内存:比 ArrayList/ArrayDeque 更重
2.3 Vector / Stack(历史遗留,不推荐)
Vector:动态数组 + 方法同步(锁开销)Stack:继承Vector,作为栈不如ArrayDeque
3) Deque / Queue(栈、队列、双端队列)
3.1 ArrayDeque(栈/队列首选)
实现类型与原理
- 底层:循环数组(环形缓冲区)
Object[] elements - 维护
head/tail下标,通过位运算/取模实现环形 - 头尾增删无需搬移,O(1)
- 扩容:数组满时重建更大数组并按顺序搬移一次
- 不允许 null(用 null 作为空槽标记)
常用 API
- 队列(FIFO):
offer,poll,peek - 栈(LIFO):
push,pop,peek - 双端:
offerFirst/offerLast,pollFirst/pollLast,peekFirst/peekLast
示例(队列 + 栈)
java
Deque<Integer> dq = new ArrayDeque<>();
// 队列
dq.offer(1);
dq.offer(2);
int q = dq.poll(); // 1
// 栈
dq.push(10);
dq.push(20);
int s = dq.pop(); // 20
什么时候用
- BFS 队列、滑窗单调队列、单调栈、括号匹配、DFS 模拟栈
- 几乎所有"栈/队列"刷题场景首选
性能
- 头尾操作:O(1)
- 通常快于 LinkedList,内存更省
- 注意:
pop()空时抛异常,安全写法用pollFirst()判断
3.2 PriorityQueue(堆,优先队列)
实现类型与原理
- 底层:数组实现的二叉堆(完全二叉树)
- 默认:小根堆(最小值在堆顶)
offer:上浮(sift up)poll:取顶后把末尾放顶再下沉(sift down)- 支持比较器自定义顺序
常用 API
offer/add,poll/remove,peeksize,isEmpty
示例(小根堆/大根堆)
java
PriorityQueue<Integer> minH = new PriorityQueue<>();
minH.offer(3); minH.offer(1);
int a = minH.poll(); // 1
PriorityQueue<Integer> maxH = new PriorityQueue<>(Collections.reverseOrder());
maxH.offer(3); maxH.offer(1);
int b = maxH.poll(); // 3
什么时候用
- TopK、实时最值、合并 K 路、Dijkstra/Prim、贪心调度
性能
- 入/出堆:O(log n)
- peek:O(1)
contains/删除任意元素:O(n)(不适合作 Set)
3.3 Queue 的并发实现(工程)
ConcurrentLinkedQueue:无锁非阻塞 FIFO(高并发)LinkedBlockingQueue/ArrayBlockingQueue:阻塞队列(线程协作)- 原理涉及 CAS/锁/条件变量,通常不用于刷题
4) Set(去重集合)
4.1 HashSet(基于 HashMap)
实现类型与原理
-
内部用
HashMap<E, Object>存储元素作为 key -
value 是固定哨兵对象(只占位)
-
哈希表:数组 + 链表/红黑树(JDK8+)
- 低碰撞:链表
- 高碰撞:链表可能树化为红黑树(降低极端退化)
-
依赖
hashCode()与equals()
常用 API
add,remove,containssize,isEmpty,clear- 遍历:for-each / iterator
示例
java
Set<String> s = new HashSet<>();
s.add("a"); s.add("b");
boolean ok = s.contains("b");
s.remove("a");
什么时候用
- 判重、集合运算、滑窗去重、两数之和判存在
性能
- 平均
add/contains/remove:O(1) - 极端:碰撞很严重会变差(JDK8+ 有树化缓解)
4.2 LinkedHashSet(HashSet + 双向链表保持插入顺序)
实现类型与原理
- 基于
LinkedHashMap - 在哈希表基础上维护双向链表,记录插入顺序
- 遍历顺序稳定
示例
java
Set<Integer> s = new LinkedHashSet<>();
s.add(3); s.add(1); s.add(2);
// 遍历:3,1,2
什么时候用
- 去重 + 需要保持插入顺序输出
性能
- 平均 O(1),略高于 HashSet(链表维护)
4.3 TreeSet(红黑树,有序集合)
实现类型与原理
- 基于
TreeMap(元素作为 key) - 红黑树:平衡二叉搜索树,保证高度 O(log n)
- 有序遍历、范围查询
常用 API(范围/邻近)
first/lastceiling/floor/higher/lowersubSet/headSet/tailSet
示例
java
TreeSet<Integer> ts = new TreeSet<>();
ts.add(3); ts.add(1); ts.add(2);
int c = ts.ceiling(2); // 2
什么时候用
- 需要有序、范围查询(>=x 最小,<=x 最大)
性能
- O(log n)
5) Map(键值映射)
5.1 HashMap(最常用)
实现类型与原理
- 底层:哈希表数组
Node<K,V>[] table - key ->
hash()-> 桶下标(index) - 桶内:链表(碰撞)或红黑树(JDK8+ 树化)
- 扩容:resize(通常成倍扩),rehash 分布到新桶
- 依赖 key 的
hashCode/equals
常用 API
put,get,removecontainsKey,getOrDefaultputIfAbsentcomputeIfAbsent(懒加载/分组常用)merge(计数合并常用)- 遍历:
entrySet/keySet/values
示例(计数 + computeIfAbsent)
java
Map<String, Integer> cnt = new HashMap<>();
String x = "a";
cnt.put(x, cnt.getOrDefault(x, 0) + 1);
Map<String, List<Integer>> group = new HashMap<>();
group.computeIfAbsent("k", k -> new ArrayList<>()).add(42);
什么时候用
- 计数、映射、缓存、索引、分组(刷题最高频)
性能
- 平均 O(1)
- 扩容/rehash 会有一次性开销
- key 自定义对象要正确实现
equals/hashCode
5.2 LinkedHashMap(有序 Map,可做 LRU)
实现类型与原理
-
基于 HashMap + 双向链表
-
可选:
- 插入顺序(默认)
- 访问顺序(
accessOrder=true)用于 LRU
-
重写
removeEldestEntry可自动淘汰最老条目
示例(访问顺序)
java
LinkedHashMap<Integer,Integer> m = new LinkedHashMap<>(16, 0.75f, true);
m.put(1,1); m.put(2,2);
m.get(1); // 1 变"最近访问"
什么时候用
- 需要稳定遍历顺序
- LRU 缓存
性能
- 平均 O(1),略高开销
5.3 TreeMap(红黑树,有序 key)
实现类型与原理
- 红黑树存 key,按自然顺序或 comparator 排序
- 支持范围查询/邻近查询
常用 API
firstKey/lastKeyceilingKey/floorKey/higherKey/lowerKeysubMap/headMap/tailMap
示例
java
TreeMap<Integer,String> tm = new TreeMap<>();
tm.put(3,"c"); tm.put(1,"a"); tm.put(2,"b");
int k = tm.ceilingKey(2); // 2
什么时候用
- 需要有序 key、区间操作(时间线/区间合并)
性能
- O(log n)
5.4 ConcurrentHashMap(并发 Map,工程)
实现原理(高层)
- 分段锁(旧)-> JDK8+ 以 CAS + synchronized 在桶级别控制
- 目标:并发读写更高吞吐、避免全表锁
示例
java
Map<String,Integer> m = new java.util.concurrent.ConcurrentHashMap<>();
m.merge("a", 1, Integer::sum);
6) BitSet(位集合,状态压缩)
实现类型与原理
- 底层:
long[](每个 long 64 位) - 通过位运算 set/get/and/or,内存极省
常用 API
set(i),clear(i),get(i)and/or/xor,flip(i)cardinality(),nextSetBit(i)
示例
java
BitSet bs = new BitSet();
bs.set(10);
boolean ok = bs.get(10);
什么时候用
- 大规模布尔标记、状态压缩 DP、位运算优化
性能
- 位运算快;空间比 boolean[] 更省(但可读性略差)
7) StringBuilder(可变字符串)
实现原理
- 底层:可扩容 char 数组(或 byte 数组,取决于 JDK 版本与编码实现)
- append 时容量不够会扩容并复制
常用 API
append,insert,delete,reversecharAt,setCharAt,lengthtoString
示例
java
StringBuilder sb = new StringBuilder();
sb.append("ab").append(123);
String s = sb.toString();
什么时候用
- 频繁拼接字符串(构造输出、路径、序列化)
性能
- 避免
String多次+产生大量临时对象
8) 刷题常用"组合结构"(用现有结构模拟)
8.1 单调栈(ArrayDeque)
- 原理:维护栈内单调性,遇到破坏单调则弹出
- 用途:下一个更大元素、最大矩形、接雨水
java
Deque<Integer> st = new ArrayDeque<>();
for (int x : nums) {
while (!st.isEmpty() && st.peek() > x) st.pop();
st.push(x);
}
8.2 单调队列(滑窗最值)
- 原理:队列内维护候选下标单调,过期弹出
java
Deque<Integer> dq = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!dq.isEmpty() && nums[dq.peekLast()] <= nums[i]) dq.pollLast();
dq.offerLast(i);
if (dq.peekFirst() <= i - k) dq.pollFirst();
if (i >= k - 1) ans[i - k + 1] = nums[dq.peekFirst()];
}
9) 性能选型对照
| 目标 | 首选结构 | 原理关键词 | 复杂度要点 |
|---|---|---|---|
| 随机访问 + 尾插 | ArrayList | 动态数组/扩容 | get O(1), 中间插删 O(n) |
| 栈/队列/双端 | ArrayDeque | 环形数组 | 头尾 O(1) |
| TopK/最值流 | PriorityQueue | 二叉堆 | offer/poll O(log n) |
| 去重/判存在 | HashSet | 哈希表 | 平均 O(1) |
| 计数/映射 | HashMap | 哈希表 | 平均 O(1) |
| 有序/范围 | TreeSet/TreeMap | 红黑树 | O(log n) |
| 插入顺序稳定 | LinkedHash* | 哈希 + 双链表 | 平均 O(1) |
| 省空间布尔标记 | BitSet | long 位数组 | 位操作 |
10) 关键坑点
-
声明类型决定可用方法
Queue q = new ArrayDeque<>();没有push/popDeque dq = new ArrayDeque<>();才有push/pop
-
ArrayDeque不允许 null(用 null 标记空槽) -
HashMap/HashSet的 key 必须正确实现equals/hashCode -
TreeMap/TreeSet的 comparator 必须满足严格弱序,否则行为异常 -
PriorityQueue默认小根堆;大根堆要 comparator -
subList是视图,修改会影响原 List