引言
集合是 Java 中最常用的数据结构容器,无论是存储对象、缓存数据还是传递参数,集合类无处不在。Java 集合框架(Java Collections Framework)提供了一套设计优良的接口和实现类,涵盖了列表、集合、队列、映射等各种数据结构。JDK 21 在集合方面带来了重要的增强------有序集合接口(Sequenced Collections),进一步统一了有序集合的访问方式。本文将系统介绍 Java 集合框架的核心接口、常用实现类、遍历方式以及 JDK 21 的新特性,帮助你全面掌握集合的使用。
下面是 Java 集合框架的整体架构图,帮助你建立全局认知:
#mermaid-svg-HCW5za5OlQVqs1EI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HCW5za5OlQVqs1EI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HCW5za5OlQVqs1EI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HCW5za5OlQVqs1EI .error-icon{fill:#552222;}#mermaid-svg-HCW5za5OlQVqs1EI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HCW5za5OlQVqs1EI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HCW5za5OlQVqs1EI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HCW5za5OlQVqs1EI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HCW5za5OlQVqs1EI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HCW5za5OlQVqs1EI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HCW5za5OlQVqs1EI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HCW5za5OlQVqs1EI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HCW5za5OlQVqs1EI .marker.cross{stroke:#333333;}#mermaid-svg-HCW5za5OlQVqs1EI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HCW5za5OlQVqs1EI p{margin:0;}#mermaid-svg-HCW5za5OlQVqs1EI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HCW5za5OlQVqs1EI .cluster-label text{fill:#333;}#mermaid-svg-HCW5za5OlQVqs1EI .cluster-label span{color:#333;}#mermaid-svg-HCW5za5OlQVqs1EI .cluster-label span p{background-color:transparent;}#mermaid-svg-HCW5za5OlQVqs1EI .label text,#mermaid-svg-HCW5za5OlQVqs1EI span{fill:#333;color:#333;}#mermaid-svg-HCW5za5OlQVqs1EI .node rect,#mermaid-svg-HCW5za5OlQVqs1EI .node circle,#mermaid-svg-HCW5za5OlQVqs1EI .node ellipse,#mermaid-svg-HCW5za5OlQVqs1EI .node polygon,#mermaid-svg-HCW5za5OlQVqs1EI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HCW5za5OlQVqs1EI .rough-node .label text,#mermaid-svg-HCW5za5OlQVqs1EI .node .label text,#mermaid-svg-HCW5za5OlQVqs1EI .image-shape .label,#mermaid-svg-HCW5za5OlQVqs1EI .icon-shape .label{text-anchor:middle;}#mermaid-svg-HCW5za5OlQVqs1EI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HCW5za5OlQVqs1EI .rough-node .label,#mermaid-svg-HCW5za5OlQVqs1EI .node .label,#mermaid-svg-HCW5za5OlQVqs1EI .image-shape .label,#mermaid-svg-HCW5za5OlQVqs1EI .icon-shape .label{text-align:center;}#mermaid-svg-HCW5za5OlQVqs1EI .node.clickable{cursor:pointer;}#mermaid-svg-HCW5za5OlQVqs1EI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HCW5za5OlQVqs1EI .arrowheadPath{fill:#333333;}#mermaid-svg-HCW5za5OlQVqs1EI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HCW5za5OlQVqs1EI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HCW5za5OlQVqs1EI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HCW5za5OlQVqs1EI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HCW5za5OlQVqs1EI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HCW5za5OlQVqs1EI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HCW5za5OlQVqs1EI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HCW5za5OlQVqs1EI .cluster text{fill:#333;}#mermaid-svg-HCW5za5OlQVqs1EI .cluster span{color:#333;}#mermaid-svg-HCW5za5OlQVqs1EI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HCW5za5OlQVqs1EI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HCW5za5OlQVqs1EI rect.text{fill:none;stroke-width:0;}#mermaid-svg-HCW5za5OlQVqs1EI .icon-shape,#mermaid-svg-HCW5za5OlQVqs1EI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HCW5za5OlQVqs1EI .icon-shape p,#mermaid-svg-HCW5za5OlQVqs1EI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HCW5za5OlQVqs1EI .icon-shape .label rect,#mermaid-svg-HCW5za5OlQVqs1EI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HCW5za5OlQVqs1EI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HCW5za5OlQVqs1EI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HCW5za5OlQVqs1EI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java 集合框架
Map(键值对)
HashMap
LinkedHashMap
TreeMap
Hashtable
Collection(单元素)
List(有序可重复)
ArrayList
LinkedList
Vector
Set(无序不可重复)
HashSet
LinkedHashSet
TreeSet
Queue(队列 FIFO)
PriorityQueue
ArrayDeque
LinkedList
读图指引 :
Collection体系存储单个元素,Map体系存储键值对;List关注顺序与索引,Set关注去重,Queue/Deque关注进出顺序,Map关注键值映射。
下面是并发集合的架构图:
#mermaid-svg-AV1lHlExtOsmdbY9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AV1lHlExtOsmdbY9 .error-icon{fill:#552222;}#mermaid-svg-AV1lHlExtOsmdbY9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AV1lHlExtOsmdbY9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AV1lHlExtOsmdbY9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AV1lHlExtOsmdbY9 .marker.cross{stroke:#333333;}#mermaid-svg-AV1lHlExtOsmdbY9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AV1lHlExtOsmdbY9 p{margin:0;}#mermaid-svg-AV1lHlExtOsmdbY9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AV1lHlExtOsmdbY9 .cluster-label text{fill:#333;}#mermaid-svg-AV1lHlExtOsmdbY9 .cluster-label span{color:#333;}#mermaid-svg-AV1lHlExtOsmdbY9 .cluster-label span p{background-color:transparent;}#mermaid-svg-AV1lHlExtOsmdbY9 .label text,#mermaid-svg-AV1lHlExtOsmdbY9 span{fill:#333;color:#333;}#mermaid-svg-AV1lHlExtOsmdbY9 .node rect,#mermaid-svg-AV1lHlExtOsmdbY9 .node circle,#mermaid-svg-AV1lHlExtOsmdbY9 .node ellipse,#mermaid-svg-AV1lHlExtOsmdbY9 .node polygon,#mermaid-svg-AV1lHlExtOsmdbY9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AV1lHlExtOsmdbY9 .rough-node .label text,#mermaid-svg-AV1lHlExtOsmdbY9 .node .label text,#mermaid-svg-AV1lHlExtOsmdbY9 .image-shape .label,#mermaid-svg-AV1lHlExtOsmdbY9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-AV1lHlExtOsmdbY9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AV1lHlExtOsmdbY9 .rough-node .label,#mermaid-svg-AV1lHlExtOsmdbY9 .node .label,#mermaid-svg-AV1lHlExtOsmdbY9 .image-shape .label,#mermaid-svg-AV1lHlExtOsmdbY9 .icon-shape .label{text-align:center;}#mermaid-svg-AV1lHlExtOsmdbY9 .node.clickable{cursor:pointer;}#mermaid-svg-AV1lHlExtOsmdbY9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AV1lHlExtOsmdbY9 .arrowheadPath{fill:#333333;}#mermaid-svg-AV1lHlExtOsmdbY9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AV1lHlExtOsmdbY9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AV1lHlExtOsmdbY9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AV1lHlExtOsmdbY9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AV1lHlExtOsmdbY9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AV1lHlExtOsmdbY9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AV1lHlExtOsmdbY9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AV1lHlExtOsmdbY9 .cluster text{fill:#333;}#mermaid-svg-AV1lHlExtOsmdbY9 .cluster span{color:#333;}#mermaid-svg-AV1lHlExtOsmdbY9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AV1lHlExtOsmdbY9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AV1lHlExtOsmdbY9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-AV1lHlExtOsmdbY9 .icon-shape,#mermaid-svg-AV1lHlExtOsmdbY9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AV1lHlExtOsmdbY9 .icon-shape p,#mermaid-svg-AV1lHlExtOsmdbY9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AV1lHlExtOsmdbY9 .icon-shape .label rect,#mermaid-svg-AV1lHlExtOsmdbY9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AV1lHlExtOsmdbY9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AV1lHlExtOsmdbY9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AV1lHlExtOsmdbY9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} java.util.concurrent 并发集合
ConcurrentHashMap
CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentLinkedQueue
BlockingQueue
ArrayBlockingQueue
LinkedBlockingQueue
并发场景 :当多个线程同时访问集合时,应使用
java.util.concurrent包下的线程安全集合,避免数据不一致和并发异常。
一、集合框架概览
Java 集合框架主要包含两大接口体系:
- Collection :存储单个元素,派生出
List、Set、Queue等子接口。 - Map :存储键值对,与
Collection接口独立,但同属集合框架。
核心接口关系图(简化):
text
Collection
├── List (有序、可重复)
├── Set (无序、不可重复)
└── Queue (队列,FIFO)
└── Deque (双端队列)
Map (键值对)
├── HashMap
├── LinkedHashMap
├── TreeMap
└── Hashtable
所有集合接口和实现类都位于 java.util 包中,并发集合位于 java.util.concurrent 包中。
二、核心接口
1. Collection 接口
Collection 是所有单元素集合的根接口,定义了基本操作:
java
Collection<String> collection = new ArrayList<>();
collection.add("apple");
collection.remove("apple");
boolean contains = collection.contains("apple");
int size = collection.size();
boolean empty = collection.isEmpty();
collection.clear();
2. List 接口
List 是有序集合,允许重复元素,可以通过索引访问。
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Java"); // 允许重复
String first = list.get(0); // "Java"
int index = list.indexOf("Python"); // 1
list.set(1, "Go"); // 替换
list.remove(0); // 按索引删除
3. Set 接口
Set 不允许重复元素,通常用于去重。
java
Set<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("apple"); // 不会添加,已存在
boolean contains = set.contains("apple"); // true
4. Queue 接口
Queue 用于队列操作,支持 FIFO(先进先出)。
java
Queue<String> queue = new LinkedList<>();
queue.offer("task1"); // 入队
queue.offer("task2");
String head = queue.poll(); // 出队,返回并移除队头,空时返回 null
String peek = queue.peek(); // 查看队头,不移除
5. Deque 接口
Deque 是双端队列,可作为栈或队列使用。
java
Deque<String> deque = new ArrayDeque<>();
deque.addFirst("a"); // 头部添加
deque.addLast("b"); // 尾部添加
deque.removeFirst(); // 头部移除
deque.removeLast(); // 尾部移除
// 作为栈使用
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); // 压栈
stack.push(2);
int top = stack.pop(); // 弹栈,2
6. Map 接口
Map 存储键值对,键唯一,值可重复。
java
Map<String, Integer> map = new HashMap<>();
map.put("apple", 3);
map.put("banana", 5);
int count = map.get("apple"); // 3
boolean hasKey = map.containsKey("apple");
map.remove("apple");
map.putIfAbsent("banana", 10); // 如果键不存在则放入
int size = map.size();
三、常用实现类详解
1. List 实现类
ArrayList
- 基于动态数组实现,支持随机访问,查询快(O(1)),增删慢(需要移动元素)。
- 非线程安全。
- 默认初始容量 10,扩容为原容量的 1.5 倍。
java
List<String> arrayList = new ArrayList<>();
LinkedList
- 基于双向链表实现,查询慢(O(n)),增删快(O(1))。
- 同时实现了
List和Deque接口,可作为列表、队列、栈使用。 - 非线程安全。
java
LinkedList<String> linkedList = new LinkedList<>();
linkedList.addFirst("first");
linkedList.addLast("last");
Vector / Stack
Vector是线程安全的ArrayList(方法加锁),性能较差,已不推荐使用。Stack继承自Vector,是线程安全的栈,推荐使用ArrayDeque替代。
2. Set 实现类
HashSet
- 基于
HashMap实现,无序,不允许重复。 - 查询和插入复杂度 O(1)。
- 元素需要正确实现
hashCode()和equals()。
java
Set<String> hashSet = new HashSet<>();
LinkedHashSet
- 继承自
HashSet,内部使用链表维护插入顺序。 - 迭代顺序与插入顺序一致。
java
Set<String> linkedHashSet = new LinkedHashSet<>();
TreeSet
- 基于红黑树实现,元素自动排序(自然顺序或自定义比较器)。
- 查询、插入复杂度 O(log n)。
- 元素必须实现
Comparable或传入Comparator。
java
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(8);
// 迭代顺序:2, 5, 8
3. Queue 实现类
PriorityQueue
- 基于优先堆实现,元素按优先级出队,不保证 FIFO。
- 默认最小堆(自然顺序),可传入比较器自定义优先级。
java
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(1);
pq.offer(2);
while (!pq.isEmpty()) {
System.out.println(pq.poll()); // 1, 2, 3
}
ArrayDeque
- 基于循环数组实现的双端队列,可作为栈或队列使用。
- 比
LinkedList性能更好,推荐使用。 - 不允许
null元素。
java
Deque<Integer> deque = new ArrayDeque<>();
4. Map 实现类
HashMap
- 基于哈希表实现,无序,键唯一。
- 查询和插入复杂度 O(1)。
- 键可以为
null(最多一个),值可以为null。 - JDK 8 以后,当链表长度超过 8 且数组长度超过 64 时,链表转为红黑树,避免退化。
java
Map<String, Integer> hashMap = new HashMap<>();
LinkedHashMap
- 继承自
HashMap,维护双向链表保持插入顺序(或访问顺序)。 - 可用于实现 LRU 缓存。
java
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
TreeMap
- 基于红黑树实现,键自动排序。
- 查询、插入复杂度 O(log n)。
- 键必须实现
Comparable或传入Comparator。
java
Map<String, Integer> treeMap = new TreeMap<>();
Hashtable
- 线程安全的
HashMap(方法加锁),性能差,已不推荐使用。 - 键和值都不允许为
null。 - 推荐使用
ConcurrentHashMap替代。
四、遍历集合
1. 增强 for 循环
java
List<String> list = List.of("a", "b", "c");
for (String s : list) {
System.out.println(s);
}
2. Iterator 迭代器
java
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("b")) {
iterator.remove(); // 安全删除
}
}
注意 :使用 for-each 时不能直接修改集合结构,否则会抛出
ConcurrentModificationException。应使用迭代器的remove()方法,或使用并发集合。
3. ListIterator(仅 List)
支持双向遍历和修改元素:
java
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
String s = listIterator.next();
if (s.equals("a")) {
listIterator.set("A"); // 替换
}
}
4. Stream API
JDK 8 引入的流式操作,适合过滤、映射、聚合等:
java
List<String> filtered = list.stream()
.filter(s -> s.length() > 1)
.map(String::toUpperCase)
.toList(); // JDK 16+
五、不可变集合与工厂方法
1. List.of() / Set.of() / Map.of()
JDK 9 引入的不可变集合工厂方法:
java
List<String> immutableList = List.of("a", "b", "c");
Set<Integer> immutableSet = Set.of(1, 2, 3);
Map<String, Integer> immutableMap = Map.of("apple", 3, "banana", 5);
这些集合是不可变的,不能添加、删除或修改元素,线程安全。
2. Collections.unmodifiableXXX()
将可变集合包装为不可变视图:
java
List<String> mutableList = new ArrayList<>();
List<String> unmodifiableList = Collections.unmodifiableList(mutableList);
六、线程安全集合
1. 同步包装器
java
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
注意:使用同步包装器时,遍历需要手动加锁,否则可能抛出异常。
2. 并发集合(推荐)
java.util.concurrent 包提供了高性能的并发集合:
- ConcurrentHashMap :线程安全的
HashMap,采用分段锁/CAS,性能优于Hashtable。 - CopyOnWriteArrayList:写时复制,适合读多写少的场景。
- CopyOnWriteArraySet :基于
CopyOnWriteArrayList实现。 - ConcurrentLinkedQueue:无界非阻塞队列。
- BlockingQueue 接口及实现(
ArrayBlockingQueue、LinkedBlockingQueue等):支持阻塞操作,适合生产者-消费者模式。
java
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
七、集合工具类
1. Collections
提供排序、查找、反转、填充等静态方法:
java
List<Integer> list = new ArrayList<>(List.of(5, 2, 8, 1));
Collections.sort(list); // 排序
Collections.reverse(list); // 反转
Collections.shuffle(list); // 打乱
int max = Collections.max(list); // 最大值
int freq = Collections.frequency(list, 2); // 元素出现次数
2. Arrays
数组与集合的转换:
java
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array); // 数组转 List(固定大小,不能增删)
List<String> mutableList = new ArrayList<>(Arrays.asList(array)); // 可变 List
String[] newArray = list.toArray(new String[0]); // List 转数组
八、JDK 21 新特性:有序集合(Sequenced Collections)
JDK 21 引入了 Sequenced Collections(JEP 431),为有序集合提供了统一的接口,解决了之前 List、SortedSet、LinkedHashSet 等有序集合访问首尾元素方式不一致的问题。
新增接口:
- SequencedCollection :继承自
Collection,新增getFirst()、getLast()、removeFirst()、removeLast()、reversed()等方法。 - SequencedSet :继承自
Set和SequencedCollection。 - SequencedMap :继承自
Map,新增firstEntry()、lastEntry()、pollFirstEntry()、pollLastEntry()、reversed()等方法。
实现类影响:
List(ArrayList、LinkedList)自动实现SequencedCollection。LinkedHashSet实现了SequencedSet。SortedSet(TreeSet)实现了SequencedSet。LinkedHashMap实现了SequencedMap。SortedMap(TreeMap)实现了SequencedMap。
使用示例:
java
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
String first = list.getFirst(); // "a"(之前需要 list.get(0))
String last = list.getLast(); // "c"(之前需要 list.get(list.size()-1))
List<String> reversed = list.reversed(); // 反向视图,不复制数据
LinkedHashSet<String> set = new LinkedHashSet<>(List.of("x", "y", "z"));
String firstSet = set.getFirst(); // "x"
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("one", 1);
map.put("two", 2);
Map.Entry<String, Integer> firstEntry = map.firstEntry(); // one=1
这些方法让有序集合的操作更加简洁和统一,尤其适合需要访问首尾元素的场景(如队列、缓存)。
九、集合选择建议
| 场景 | 推荐集合 |
|---|---|
| 需要随机访问、频繁查询 | ArrayList |
| 频繁在头部/中间插入删除 | LinkedList 或 ArrayDeque |
| 去重且不关心顺序 | HashSet |
| 去重且需要保持插入顺序 | LinkedHashSet |
| 去重且需要排序 | TreeSet |
| 键值对存储、快速查找 | HashMap |
| 键值对且需要插入顺序 | LinkedHashMap |
| 键值对且需要排序 | TreeMap |
| 需要队列 FIFO | ArrayDeque 或 LinkedList |
| 需要优先级队列 | PriorityQueue |
| 需要栈 LIFO | ArrayDeque(推荐) |
| 多线程并发访问 | ConcurrentHashMap、CopyOnWriteArrayList 等 |
| 不可变集合 | List.of()、Set.of()、Map.of() |
十、最佳实践
- 优先使用接口类型声明集合 ,例如
List<String> list = new ArrayList<>();,方便替换实现。 - 指定初始容量:对于已知大小的集合,创建时指定容量可减少扩容开销。
- 避免在循环中使用
+拼接字符串,同理,在循环中频繁增删元素时选择合适的数据结构。 - 正确实现
hashCode()和equals():使用HashSet或HashMap时,自定义对象必须正确覆写这两个方法。 - 使用泛型:避免原始类型,确保类型安全。
- 遍历时修改集合要小心 :使用迭代器的
remove()方法,或使用并发集合,或先收集待删除元素再统一删除。 - 优先使用不可变集合 :对于不需要修改的集合,使用
List.of()等工厂方法,更安全且节省内存。 - 利用 JDK 21 的有序集合方法 :访问首尾元素时使用
getFirst()/getLast(),代码更清晰。
十一、热点面试题精讲
集合是 Java 面试的高频考点,下面梳理几类最常见的面试题,并给出答题要点。
1. ArrayList 与 LinkedList 的区别?
| 对比维度 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 随机访问 | O(1),通过索引直接定位 | O(n),需从头遍历 |
| 头部/中间插入删除 | O(n),需移动元素 | O(1),只需改指针(定位到节点后) |
| 内存占用 | 连续内存,更紧凑 | 每个节点额外存储前后指针 |
| 适用场景 | 查询多、增删少 | 频繁在头部/中间增删 |
答题要点 :先讲底层结构差异,再引申到复杂度差异,最后落到「查询多选 ArrayList,增删多选 LinkedList」的结论。可补充:LinkedList 同时实现了 Deque,可作队列/栈使用。
2. HashMap 的底层实现原理?
答题要点(按版本演进回答):
- JDK 7 及以前:数组 + 链表,采用头插法。
- JDK 8 及以后:数组 + 链表 + 红黑树。当链表长度超过 8 且数组长度超过 64 时,链表转为红黑树,将最坏时间复杂度从 O(n) 优化为 O(log n)。
- put 流程 :计算 key 的
hashCode()→ 扰动函数二次散列 →(n - 1) & hash定位桶下标 → 若为空直接放入,否则遍历链表/红黑树,key 相同则覆盖,否则插入。 - 扩容机制 :默认初始容量 16,负载因子 0.75,当元素个数超过
容量 × 负载因子时扩容为原来的 2 倍。
java
// 扰动函数(JDK 8)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
3. HashMap 与 Hashtable、ConcurrentHashMap 的区别?
| 对比维度 | HashMap | Hashtable | ConcurrentHashMap |
|---|---|---|---|
| 线程安全 | 否 | 是(全表加锁) | 是(分段锁/CAS) |
| 允许 null 键值 | 是 | 否 | 否 |
| 性能 | 最高 | 差 | 高 |
| 推荐度 | 单线程使用 | 不推荐 | 并发场景推荐 |
答题要点 :强调 Hashtable 用 synchronized 锁住整个表,并发度低;ConcurrentHashMap 在 JDK 8 后采用 CAS + synchronized 锁桶,并发度大幅提升。同时指出 ConcurrentHashMap 不允许 null 键值,是为了避免并发下的二义性。
4. 为什么 HashMap 的容量是 2 的幂次方?
答题要点:
- 计算桶下标时使用
(n - 1) & hash代替取模运算,位运算更快。 - 当
n为 2 的幂时,(n - 1) & hash等价于hash % n,且结果分布更均匀。 - 扩容时元素要么留在原位置,要么移动到「原位置 + 旧容量」处,重排高效。
5. HashSet 如何保证元素不重复?
答题要点 :HashSet 底层基于 HashMap 实现,元素作为 HashMap 的 key 存储,value 统一为一个固定对象。判断重复依赖 hashCode() 和 equals():先比较哈希值,哈希相同再调用 equals() 确认。因此自定义对象放入 HashSet 时必须正确覆写这两个方法。
6. 遍历集合时如何安全地删除元素?
答题要点 :for-each 遍历时直接删除会抛出 ConcurrentModificationException。推荐三种方式:
java
// 方式一:使用 Iterator 的 remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) {
it.remove();
}
}
// 方式二:使用 removeIf()(JDK 8+,最简洁)
list.removeIf(s -> s.equals("b"));
// 方式三:先收集再统一删除
List<String> toRemove = list.stream()
.filter(s -> s.equals("b"))
.toList();
list.removeAll(toRemove);
7. 如何实现一个 LRU 缓存?
答题要点 :LinkedHashMap 支持访问顺序(accessOrder=true),重写 removeEldestEntry() 即可实现 LRU:
java
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true); // accessOrder=true,按访问顺序
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity; // 超过容量时移除最久未访问的条目
}
}
8. 什么是快速失败(fail-fast)与安全失败(fail-safe)?
答题要点:
- fail-fast(快速失败) :
ArrayList、HashMap等普通集合在遍历时若结构被修改,立即抛出ConcurrentModificationException。原理是迭代器维护modCount计数器,遍历时校验是否被修改。 - fail-safe(安全失败) :
CopyOnWriteArrayList、ConcurrentHashMap等并发集合在遍历时基于快照,不会抛出异常,但遍历结果可能不是最新的。
总结
Java 集合框架提供了丰富的数据结构和算法支持,掌握核心接口与实现类的特性,能够帮助我们在不同场景下做出合适的选择。JDK 21 引入的有序集合接口进一步提升了 API 的一致性,让代码更加简洁。在实际开发中,应结合性能、线程安全和功能需求,选择最合适的集合类,并遵循最佳实践,编写健壮、高效的代码。