Java 集合框架完全指南:从核心接口到 JDK 21 有序集合新特性

引言

集合是 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()

十、最佳实践

  1. 优先使用接口类型声明集合 ,例如 List<String> list = new ArrayList<>();,方便替换实现。
  2. 指定初始容量:对于已知大小的集合,创建时指定容量可减少扩容开销。
  3. 避免在循环中使用 + 拼接字符串,同理,在循环中频繁增删元素时选择合适的数据结构。
  4. 正确实现 hashCode() 和 equals() :使用 HashSet 或 HashMap 时,自定义对象必须正确覆写这两个方法。
  5. 使用泛型:避免原始类型,确保类型安全。
  6. 遍历时修改集合要小心 :使用迭代器的 remove() 方法,或使用并发集合,或先收集待删除元素再统一删除。
  7. 优先使用不可变集合 :对于不需要修改的集合,使用 List.of() 等工厂方法,更安全且节省内存。
  8. 利用 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 的一致性,让代码更加简洁。在实际开发中,应结合性能、线程安全和功能需求,选择最合适的集合类,并遵循最佳实践,编写健壮、高效的代码。

相关推荐
小羊没烦恼!5 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里5 天前
java中的继承和多态的区别
java
小羊没烦恼!5 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
rockmelodies5 天前
# Windows Server2008 R2 Standard(SP1镜像U盘)重置本地管理员密码【完整详细步骤】
windows·密码破解
譕痕5 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码5 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖5 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时5 天前
01-06-A-JVM排查实战详解
java·jvm
vipxieliang5 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
ba_pi5 天前
mysql 查询所有表名并授权
java