1. ArrayDeque —— 更好用的队列/栈
ArrayDeque 是一个基于数组实现的双端队列,既可以当 队列 也可以当 栈。比 LinkedList 更快更轻量。
常用 API:
addFirst(e)/addLast(e):从头/尾添加offerFirst(e)/offerLast(e):和 add 类似,但失败时不会抛异常removeFirst()/removeLast():移除并返回头/尾元素pollFirst()/pollLast():移除并返回头/尾元素,失败返回 nullpeekFirst()/peekLast():查看头/尾元素但不移除push(e)/pop():栈的用法(压栈/弹栈)
例子:
ArrayDeque<String> deque = new ArrayDeque<>();
deque.addFirst("A");
deque.addLast("B");
deque.push("C"); // 相当于 addFirst
System.out.println(deque); // [C, A, B]
System.out.println(deque.pop()); // C
System.out.println(deque.pollLast()); // B
总结:用 ArrayDeque 替代 Stack 或 LinkedList,性能更好。
2. HashMap —— 最常用的键值对容器
HashMap 是基于哈希表实现的键值对容器,查找、插入、删除 都是 O(1) 平均复杂度。
常用 API:
-
put(key, value):插入或更新 -
get(key):获取值 -
containsKey(key)/containsValue(value):是否包含 -
remove(key):删除 -
forEach((k,v) -> {...}):遍历 -
现代 API(很重要!):
computeIfAbsent(key, k -> new ...)computeIfPresent(key, (k,v) -> ...)merge(key, value, (oldVal, newVal) -> ...)getOrDefault(key, defaultValue)
例子:
Map> map = new HashMap<>();
// 如果 key 不存在,就创建一个新的 ArrayList
map.computeIfAbsent("nums", k -> new ArrayList<>()).add(1);
map.computeIfAbsent("nums", k -> new ArrayList<>()).add(2);
System.out.println(map); // {nums=[1, 2]}
// 如果 key 存在,修改它的值
map.computeIfPresent("nums", (k, v) -> {
v.add(3);
return v;
});
System.out.println(map); // {nums=[1, 2, 3]}
// 合并值
map.merge("nums", Arrays.asList(4), (oldVal, newVal) -> {
oldVal.addAll(newVal);
return oldVal;
});
System.out.println(map); // {nums=[1, 2, 3, 4]}
总结:computeIfAbsent 在处理「一键多值」的场景(比如 Map
3. TreeMap —— 自动排序的 Map
TreeMap 是基于 红黑树 实现的有序 Map,会按照 key 的自然顺序(或自定义 Comparator)排序。
常用 API:
-
和
HashMap基本一致(put/get/forEach) -
额外的有序 API:
firstKey()/lastKey():最小/最大 keyhigherKey(k)/lowerKey(k):比 k 大/小的最近 keysubMap(from, to):区间子集
例子:
TreeMap treeMap = new TreeMap<>();
treeMap.put(3, "C");
treeMap.put(1, "A");
treeMap.put(2, "B");
System.out.println(treeMap); // {1=A, 2=B, 3=C}
System.out.println(treeMap.firstKey()); // 1
System.out.println(treeMap.higherKey(1)); // 2
总结:当你需要 有序的 Map,首选 TreeMap。
4. HashSet —— 不重复的集合
HashSet 本质上是 HashMap 的一个特殊实现,只存 key,不存 value。
特点是 无序、不可重复。
常用 API:
add(e):添加remove(e):删除contains(e):是否存在forEach(e -> {...}):遍历
例子:
Set set = new HashSet<>();
set.add("A");
set.add("B");
set.add("A"); // 重复元素不会加入
System.out.println(set); // [A, B]
System.out.println(set.contains("A")); // true
总结:快速去重,首选 HashSet。
5. TreeSet —— 自动排序的 Set
TreeSet 是基于 TreeMap 的有序集合,元素自动排序。
常用 API:
-
和
HashSet类似(add/remove/contains) -
排序特有:
first()/last():最小/最大元素higher(e)/lower(e):比 e 大/小的最近元素subSet(from, to):区间子集
例子:
TreeSet treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
System.out.println(treeSet); // [1, 2, 3]
System.out.println(treeSet.first()); // 1
System.out.println(treeSet.higher(1)); // 2
总结:当你需要 排序+去重 的集合,用 TreeSet。
6. PriorityQueue —— Java 的优先队列
PriorityQueue 是基于 堆(Heap) 实现的优先队列。它的特点是:
- 默认是 小顶堆(最小的元素优先)
- 插入和取出都是 O(logN)
常用 API:
add(e)/offer(e):添加元素poll():取出并移除堆顶元素(最小/最大)peek():只查看堆顶元素isEmpty():是否为空
默认行为(小顶堆)
PriorityQueue pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(1);
pq.offer(2);
System.out.println(pq.poll()); // 1
System.out.println(pq.poll()); // 2
System.out.println(pq.poll()); // 3
默认是小顶堆,所以每次 poll() 都取最小的元素。
自定义排序 —— 从根本上理解 Comparator
PriorityQueue、TreeSet、TreeMap 都支持 自定义排序。
关键点是:
-
排序是通过 Comparator 控制的
-
Comparator的本质就是一个函数(a, b) -> int- 返回负数:a 在前
- 返回 0:相等
- 返回正数:b 在前
例子 1:大顶堆(最大优先)
PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a);
maxHeap.offer(3);
maxHeap.offer(1);
maxHeap.offer(2);
System.out.println(maxHeap.poll()); // 3
System.out.println(maxHeap.poll()); // 2
System.out.println(maxHeap.poll()); // 1
这里 (a, b) -> b - a 实际上就是 把比较反过来,从而实现大顶堆。
例子 2:字符串长度排序
PriorityQueue pq = new PriorityQueue<>(
(a, b) -> a.length() - b.length()
);
pq.offer("apple");
pq.offer("pear");
pq.offer("banana");
System.out.println(pq.poll()); // pear (3个字母)
System.out.println(pq.poll()); // apple (5个字母)
System.out.println(pq.poll()); // banana (6个字母)
这里就按照字符串长度来排。
例子 3:TreeSet 自定义排序(避免去重导致的坑)
TreeSet set = new TreeSet<>(
(a, b) -> {
int cmp = a.length() - b.length();
return cmp != 0 ? cmp : a.compareTo(b);
}
);
set.add("pear");
set.add("apple");
set.add("banana");
System.out.println(set); // [pear, apple, banana]
这里先按照长度排,如果长度相等再按字典序排。
(否则 TreeSet 会认为长度相等的元素就是同一个,导致被“去重”掉)
自定义排序的记忆法
把 Comparator 理解成一句话:
- 想让小的排前面:
a - b - 想让大的排前面:
b - a - 想按某个属性:
a.attr - b.attr - 想多重排序:先比一个,如果相等再比另一个
这样就不会再混淆了
写在最后
| 类 | 底层结构 | 是否有序 | 是否允许重复 | 常用 API | 典型场景 |
|---|---|---|---|---|---|
| ArrayDeque | 数组 | 按插入顺序 | 允许 | addFirst/Last、pollFirst/Last、push/pop | 栈/队列 |
| HashMap | 哈希表 | 无序 | key 不可重复 | put/get/remove、computeIfAbsent | 键值对存储 |
| TreeMap | 红黑树 | key 有序 | key 不可重复 | firstKey、higherKey、subMap | 区间查找 |
| HashSet | 哈希表 | 无序 | 不可重复 | add/remove/contains | 去重 |
| TreeSet | 红黑树 | 有序 | 不可重复 | first、higher、subSet | 排序+去重 |
| PriorityQueue | 堆 | 局部有序(堆顶最优) | 允许 | offer/poll/peek | 优先任务调度 |