Java Collections Framework 详解
Java Collections Framework (JCF) 是 Java 语言中一套标准的接口、类和算法的集合,用于存储和操作对象组。它提供了一系列高性能的数据结构实现,如列表、集合、映射等,这些数据结构被精心设计以满足各种常见的数据存储和检索需求。JCF 旨在统一管理、操作对象集合的 API,提高 Java 程序的生产力和性能。
核心思想:JCF 提供了一致的 API 来处理不同类型的数据结构,如列表、集合和映射,并与用于操作这些结构的标准算法(排序、搜索等)相结合。它允许开发者专注于业务逻辑,而不是底层数据结构的实现细节。
一、JCF 的发展历程与目的
在 JCF 出现之前 (即 JDK 1.2 之前),Java 提供了 Vector、Hashtable 等线程安全的旧式集合类,以及数组。这些类功能有限,API 不统一,且缺乏泛型支持,使用起来不便。
JCF 的引入旨在解决以下问题:
- 统一 API:提供一套统一的接口和类,使得操作不同类型的集合具有相似的代码模式。
- 提高性能:提供高效的数据结构实现,并利用算法优化常见操作。
- 减少学习成本:开发者只需学习一套 API 就可以处理各种集合类型。
- 增强互操作性:不同组件之间可以无缝地传递和处理集合。
- 支持泛型 (Generics):从 Java 5 开始,JCF 与泛型结合,提供了更好的类型安全和代码可读性。
- 可扩展性:提供抽象接口,允许开发者自定义集合实现。
二、JCF 核心接口体系
JCF 主要由三大核心接口及其子接口构成:Collection、List、Set 和 Map。
graph LR
A[Iterable<E>] --> B[Collection<E>]
B --> C[List<E>]
B --> D[Set<E>]
B --> E[Queue<E>]
C --> C1[ArrayList<E>]
C --> C2[LinkedList<E>]
C --> C3[Vector<E>]
C --> C4[Stack<E>]
D --> D1[HashSet<E>]
D --> D2[LinkedHashSet<E>]
D --> D3[SortedSet<E>]
D3 --> D4[TreeSet<E>]
E --> E1[PriorityQueue<E>]
E --> E2[Deque<E>]
E2 --> E3[ArrayDeque<E>]
E2 --> C2_Linked[LinkedList<E>]
F[Map<K, V>]
F --> F1[HashMap<K, V>]
F --> F2[LinkedHashMap<K, V>]
F --> F3[SortedMap<K, V>]
F3 --> F4[TreeMap<K, V>]
F --> F5[Hashtable<K, V>]
2.1 Iterable 接口
Iterable<E>是所有集合类的父接口,它不是Collection的子接口。- 它只包含一个方法
iterator(),返回一个Iterator<E>对象。 - 这使得所有实现
Iterable接口的类都可以使用增强for循环 (for-each) 遍历。
2.2 Collection 接口
Collection<E>是所有单值集合的根接口,定义了操作对象组的基本行为。- 特性:
- 表示一组对象,这些对象被称为集合的元素 (Elements)。
- 不保证元素的顺序。
- 可能包含重复元素 (取决于具体实现,如
List) 或不允许重复 (如Set)。
- 常用方法:
add(),remove(),contains(),size(),isEmpty(),toArray(),clear()等。
2.3 List 接口 (有序、可重复)
List<E>是Collection的子接口,表示一个有序的元素序列。- 特性:
- 元素有严格的插入顺序,可以通过索引访问。
- 允许包含重复元素。
- 允许包含
null元素。
- 常用实现类:
ArrayList(基于数组实现)- 特点:底层是动态数组,查询 / 随机访问快 (
get(index))。 - 增删效率:插入或删除元素 (特别是中间位置) 效率较低,因为可能涉及到大量元素移动。
- 内存:连续内存,空间利用率高。
- 线程安全:非线程安全。
- 特点:底层是动态数组,查询 / 随机访问快 (
LinkedList(基于双向链表实现)- 特点:底层是双向链表,插入 / 删除效率高 (只需改变指针)。
- 查询效率:随机访问效率低 (
get(index)需遍历)。 - 内存:非连续内存,每个节点额外存储前后指针。
- 线程安全:非线程安全。
Vector(旧式线程安全类)- 特点:与
ArrayList类似,底层是动态数组,但所有方法都是synchronized的,因此是线程安全的。 - 效率:因为同步开销,性能比
ArrayList低。 - 遗留:通常推荐使用
Collections.synchronizedList(new ArrayList<>())或CopyOnWriteArrayList替代。
- 特点:与
Stack(继承Vector的堆栈)- 特点:实现了后进先出 (LIFO) 的堆栈功能,但因继承
Vector且 API 设计不佳,不推荐使用。 - 替代:推荐使用
Deque接口的实现,如ArrayDeque。
- 特点:实现了后进先出 (LIFO) 的堆栈功能,但因继承
示例 (Java List):
1 | import java.util.ArrayList; |
2.4 Set 接口 (无序、不重复)
Set<E>是Collection的子接口,表示一个不允许包含重复元素的集合。- 特性:
- 不保证元素的顺序。
- 最多包含一个
null元素。 - 判断元素重复基于
equals()方法。
- 常用实现类:
HashSet(基于哈希表实现)- 特点:查询、插入、删除效率高 (O(1) 平均)。
- 顺序:不保证元素的迭代顺序,可能与插入顺序不同。
- 判重:依赖元素的
hashCode()和equals()方法。 - 线程安全:非线程安全。
LinkedHashSet(基于哈希表和双向链表实现)- 特点:兼具
HashSet的快速查找和LinkedList的插入顺序维护。 - 顺序:维护元素的插入顺序 (迭代顺序与插入顺序一致)。
- 线程安全:非线程安全。
- 特点:兼具
TreeSet(基于红黑树实现)- 特点:存储的元素自动进行排序 (自然排序或自定义比较器)。
- 效率:查询、插入、删除效率为 O(log N)。
- 判重 / 排序:要求元素实现
Comparable接口,或在创建TreeSet时提供Comparator。 - 线程安全:非线程安全。
示例 (Java Set):
1 | import java.util.HashSet; |
2.5 Queue 接口 (队列)
Queue<E>是Collection的子接口,表示一个先进先出 (FIFO) 的元素集合。- 特性:
- 常用于存储在处理之前暂存的元素。
- 提供特定的插入 (
offer())、移除 (poll()) 和检查 (peek()) 元素的方法。
- 常用实现类:
PriorityQueue(基于堆实现)- 特点:队列中的元素会根据其自然排序或提供的
Comparator进行排序,优先级高的元素优先出队。 - 线程安全:非线程安全。
- 特点:队列中的元素会根据其自然排序或提供的
Deque接口 (继承自Queue,双端队列)- 特点:双端队列,两端都可进可出。既可以作为队列 (FIFO),也可以作为栈 (LIFO)。
- 常用实现:
ArrayDeque,LinkedList。 ArrayDeque是高效的非线程安全双端队列,作为栈 (Stack) 和队列 (Queue) 的替代品优于Stack和LinkedList。
示例 (Java Queue 和 Deque):
1 | import java.util.ArrayDeque; |
2.6 Map 接口 (键值对)
Map<K, V>表示一个将键 (Key) 映射到值 (Value) 的对象。- 特性:
- 键是唯一的,不能重复。
- 一个键只能映射到一个值。
- 值可以重复。
- 允许一个
null键和多个null值 (取决于具体实现)。 - 判断键重复基于键的
hashCode()和equals()方法。
- 常用方法:
put(K key, V value),get(Object key),remove(Object key),containsKey(Object key),containsValue(Object value),size(),isEmpty(),keySet(),values(),entrySet()。 - 常用实现类:
HashMap(基于哈希表实现)- 特点:查找、插入、删除效率高 (O(1) 平均)。
- 顺序:不保证键值对的迭代顺序。
- 线程安全:非线程安全。
LinkedHashMap(基于哈希表和双向链表实现)- 特点:兼具
HashMap的快速查找和维护插入顺序。 - 顺序:维护键值对的插入顺序 (迭代顺序与插入顺序一致)。
- 线程安全:非线程安全。
- 特点:兼具
TreeMap(基于红黑树实现)- 特点:所有键值对按键的自然排序或自定义
Comparator进行排序。 - 效率:查找、插入、删除效率为 O(log N)。
- 线程安全:非线程安全。
- 特点:所有键值对按键的自然排序或自定义
Hashtable(旧式线程安全类)- 特点:与
HashMap类似,但所有方法都是synchronized的,因此是线程安全的。 - 效率:因同步开销,性能比
HashMap低。 - 遗留:不允许
null键和null值。通常推荐使用ConcurrentHashMap替代。
- 特点:与
示例 (Java Map):
1 | import java.util.HashMap; |
三、Collections 工具类
java.util.Collections 是一个工具类,注意它不是 Collection 接口,它包含了一系列静态方法,用于对集合进行操作、搜索、排序、同步化、包装或创建不可变集合等。
- 排序:
sort(List)(使用元素的自然顺序) 或sort(List, Comparator)。 - 搜索:
binarySearch(List, Object)。 - 反转:
reverse(List)。 - 混排:
shuffle(List)。 - 同步包装器:
synchronizedList(List),synchronizedSet(Set),synchronizedMap(Map)。 - 不可变集合:
unmodifiableList(List),unmodifiableSet(Set),unmodifiableMap(Map)。
示例 (Java Collections):
1 | import java.util.ArrayList; |
四、迭代器 (Iterator)
Iterator<E>是遍历集合元素的核心接口。- 所有
Collection及其子接口都提供了iterator()方法来获取Iterator实例。 - 常用方法:
hasNext():检查集合中是否还有下一个元素。next():返回集合中的下一个元素,并将迭代器向前移动。remove():从集合中移除next()方法返回的最后一个元素。
Iterator是一种安全的遍历方式,因为它允许你在遍历过程中安全地移除元素,避免ConcurrentModificationException(但只能通过迭代器自身的remove()方法)。
示例 (Java Iterator):
1 | import java.util.ArrayList; |
五、线程安全集合
JCF 中的大部分实现(如 ArrayList, HashSet, HashMap)都是非线程安全的。
在多线程环境下使用这些集合时,需要进行外部同步,或者使用专门的线程安全集合。
- 旧式集合:
Vector,Hashtable(已过时,性能较差)。 - 包装器:使用
Collections.synchronizedList(),synchronizedSet(),synchronizedMap()方法对非线程安全的集合进行包装。 - Java 并发包 (
java.util.concurrent):提供了一系列高性能的线程安全集合,推荐在多线程环境下使用:ConcurrentHashMap:高性能的并发 Map。CopyOnWriteArrayList/CopyOnWriteArraySet:读操作无需加锁,写操作复制底层数组 (适合读多写少场景)。BlockingQueue及其实现 (ArrayBlockingQueue,LinkedBlockingQueue):用于生产者-消费者模式。
六、遍历集合的方式
- 增强
for循环 (for-each):适用于所有实现了Iterable接口的集合。1
2
3for (String item : myList) {
System.out.println(item);
} Iterator遍历:最通用的遍历方式,可以在遍历过程中安全移除元素。1
2
3
4Iterator<String> it = myList.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}- Lambda 表达式 (Java 8+):结合
forEach()方法,简洁优雅。1
2
3myList.forEach(item -> System.out.println(item));
// 或方法引用
myList.forEach(System.out::println); - 普通
for循环:只适用于List接口 (因为可以按索引访问)。1
2
3for (int i = 0; i < myList.size(); i++) {
System.out.println(myList.get(i));
}
七、总结与选择建议
Java Collections Framework 提供了一整套丰富且高效的数据结构和算法,是 Java 编程中不可或缺的部分。理解其核心接口、常用实现类及其特性对于编写高效、健壮的 Java 代码至关重要。
选择建议:
- 需要顺序和重复元素:使用
List。- 读多写少、随机访问多:
ArrayList。 - 频繁插入 / 删除元素 (特别是两端):
LinkedList。
- 读多写少、随机访问多:
- 需要唯一元素:使用
Set。- 对顺序无要求,追求高性能:
HashSet。 - 需要维护插入顺序:
LinkedHashSet。 - 需要自动排序:
TreeSet。
- 对顺序无要求,追求高性能:
- 需要键值对映射:使用
Map。- 对键值对顺序无要求,追求高性能:
HashMap。 - 需要维护插入顺序:
LinkedHashMap。 - 需要按键自动排序:
TreeMap。
- 对键值对顺序无要求,追求高性能:
- 需要队列或栈行为:使用
Queue或Deque。- 普通 FIFO 队列:
LinkedList(作为Queue实现)。 - 优先级队列:
PriorityQueue。 - 高效的 LIFO 栈或双端队列:
ArrayDeque。
- 普通 FIFO 队列:
- 多线程环境:避免使用非同步集合,转而考虑:
java.util.concurrent包下的并发集合类 (如ConcurrentHashMap,CopyOnWriteArrayList)。Collections.synchronizedXxx()包装器。
通过深入理解 JCF,开发者可以更好地选择合适的数据结构来解决实际问题,从而编写出更高质量的 Java 应用程序。
