01_Java集合框架体系结构与核心接口原理
约 2849 字大约 10 分钟
2025-11-17
集合框架入门导读
什么是集合?为什么需要集合?
简单来说,集合就是用来存储多个对象的容器。在Java中,我们使用变量存储单个数据,但当需要处理一组相关的数据时,使用集合会更加方便和高效。
集合相比数组的优势:
- 自动扩容:集合会根据需要自动调整大小,不需要手动管理容量
- 丰富的操作:提供了添加、删除、查找、排序等多种便捷方法
- 类型安全:支持泛型,可以在编译时检查类型错误
- 面向接口编程:通过统一的接口提供不同的实现,适应不同场景
集合框架面试常见问题
1. Java集合框架的主要接口有哪些?
Collection:所有单列集合的根接口List:有序可重复的集合Set:无序不可重复的集合Map:键值对映射集合Queue:队列接口Deque:双端队列接口
2. Collection和Collections的区别?
Collection是接口,表示集合,定义了集合的基本操作Collections是工具类,提供了操作集合的静态方法(如排序、查找等)
3. 如何选择合适的集合?
- 需要有序可重复:选
List接口下的实现(ArrayList/LinkedList) - 需要无序不可重复:选
Set接口下的实现(HashSet/TreeSet) - 需要键值对:选
Map接口下的实现(HashMap/TreeMap) - 需要队列操作:选
Queue接口下的实现 - 需要双端操作:选
Deque接口下的实现
集合框架的整体结构(零基础理解版)
Java集合框架主要分为两大类:
- 单列集合(
Collection):存储单个元素的集合- List:有序、可重复的集合(如
ArrayList、LinkedList) - Set:无序、不可重复的集合(如
HashSet、TreeSet) - Queue:队列,先进先出的集合(如
ArrayDeque、PriorityQueue)
- List:有序、可重复的集合(如
- 双列集合(
Map):存储键值对的集合,通过键快速查找值(如HashMap、TreeMap)
如何选择合适的集合?
- 如果需要快速随机访问元素,选择 ArrayList
- 如果需要频繁插入删除元素,选择 LinkedList
- 如果需要存储不重复的元素,选择 HashSet
- 如果需要键值对存储和查询,选择 HashMap
- 如果需要队列操作(先进先出),选择 ArrayDeque
学习路径
集合框架定义
Java集合框架是为了更方便地存储和操作对象而设计的一组接口、实现类和工具类。它提供了统一的方式来管理对象组,并支持各种常见的数据结构和操作。
核心优势
- 统一的接口:所有集合都有一致的操作方式
- 丰富的数据结构:提供各种常见数据结构的实现
- 高效的算法:内置高效的排序、搜索等算法
- 自动内存管理:自动处理对象的引用和垃圾回收
- 线程安全支持:提供线程安全的集合实现
体系结构概览
主要实现类关系
核心接口
Iterable接口
Iterable是Java集合框架的根接口,所有集合类都间接实现了该接口。
public interface Iterable<T> {
// 获取迭代器
Iterator<T> iterator();
// 遍历元素
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
// 获取分割迭代器
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}Collection接口
Collection是集合框架的核心接口,定义了所有集合共有的操作。
public interface Collection<E> extends Iterable<E> {
// 基本操作
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
// 修改操作
boolean add(E e);
boolean remove(Object o);
// 批量操作
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
// 相等性和哈希码
boolean equals(Object o);
int hashCode();
// Java 8+ 方法
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
// 并行处理相关方法
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}List接口
List接口代表有序可重复的集合,允许通过索引访问元素。
核心特征:
- 有序:元素按照插入顺序或自定义顺序排列
- 可重复:允许包含重复的元素
- 索引访问:支持通过索引快速访问元素
Set接口
Set接口代表无序不重复的集合,元素唯一。
核心特征:
- 无序:元素不保证顺序(
LinkedHashSet除外) - 不重复:元素不可重复,依赖
equals()和hashCode() - 高效查找:提供高效的元素查找功能
Map接口
Map接口代表键值对映射,通过键查找值。
核心特征:
- 键值对:以键值对形式存储数据
- 键唯一:键不可重复,值可重复
- 高效查找:通过键快速查找值
public interface Map<K, V> {
// 基本操作
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);
// 批量操作
void putAll(Map<? extends K, ? extends V> m);
void clear();
// 视图操作
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
// 嵌套接口
interface Entry<K, V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
}
// Java 8+ 方法
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
}Queue接口
Queue接口代表队列,支持FIFO(先进先出)操作。
核心特征:
- 队列操作:提供入队和出队操作
- 优先级:支持优先级队列
- 阻塞操作:支持阻塞队列操作
Deque接口
Deque接口代表双端队列,支持两端元素的添加和删除。
核心特征:
- 双端操作:支持从两端添加和删除元素
- 队列/栈:同时支持队列和栈操作
- 高效操作:提供高效的双端操作
主要子接口对比
| 特性 | List | Set | Map | Queue |
|---|---|---|---|---|
| 数据结构 | 有序列表 | 集合 | 键值映射 | 队列 |
| 元素重复 | 允许 | 不允许 | 值允许重复,键不允许 | 可选,取决于实现 |
| 索引访问 | 支持 | 不支持 | 通过键访问值 | 通常不支持 |
| 主要实现 | ArrayList, LinkedList | HashSet, TreeSet | HashMap, TreeMap | PriorityQueue, ArrayDeque |
| 适用场景 | 有序存储,随机访问 | 去重,无序存储 | 键值映射,查找 | 顺序处理,任务调度 |
性能特征对比
时间复杂度
| 操作 | ArrayList | LinkedList | HashSet | TreeSet | HashMap | TreeMap |
|---|---|---|---|---|---|---|
| 随机访问 | O(1) | O(n) | - | - | O(1) 平均 | O(log n) |
| 添加元素 | O(1) 平均 | O(1) | O(1) 平均 | O(log n) | O(1) 平均 | O(log n) |
| 删除元素 | O(n) | O(1) | O(1) 平均 | O(log n) | O(1) 平均 | O(log n) |
| 查找元素 | O(n) | O(n) | O(1) 平均 | O(log n) | O(1) 平均 | O(log n) |
| 遍历 | O(n) | O(n) | O(n) | O(n) | O(n) | O(n) |
空间复杂度
- ArrayList:O(n),底层数组存储,空间利用率较高
- LinkedList:O(n),每个元素需要额外的前后指针,空间开销更大
- HashSet:O(n),基于HashMap实现,额外存储一个虚拟值
- TreeSet:O(n),基于TreeMap实现,红黑树结构需要额外空间
- HashMap:O(n),存储键值对,需要处理哈希冲突
- TreeMap:O(n),红黑树结构,每个节点存储键值对和颜色信息
集合选择决策流程
常见陷阱和注意事项
哈希冲突问题
// 错误的实现 - 会导致哈希冲突
class BadPerson {
private String name;
private int age;
// 仅使用name计算hashCode
@Override
public int hashCode() {
return name.hashCode();
}
// 但equals使用name和age
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
BadPerson person = (BadPerson) obj;
return age == person.age && Objects.equals(name, person.name);
}
}equals和hashCode一致性
正确示例:
class GoodPerson {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GoodPerson that = (GoodPerson) o;
return age == that.age && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}线程安全问题
// 非线程安全集合在多线程环境中的风险
List<String> list = new ArrayList<>();
// 在多线程环境中使用可能导致ConcurrentModificationException自动装箱和拆箱开销
// 避免频繁的自动装箱和拆箱
// 推荐使用原始类型流
IntStream.range(0, 10000).forEach(i -> list.add(i));最佳实践
- 选择合适的集合类型:根据具体需求和性能考虑选择合适的集合实现
- 预设容量:预估集合大小并设置初始容量,避免频繁扩容
- 使用泛型:使用泛型提供类型安全并避免强制类型转换
- 优先使用接口:使用接口声明集合变量,方便未来切换实现
- 注意线程安全:在多线程环境中使用线程安全的集合实现
- 使用Java 8+新特性:利用
Stream API、方法引用等提高代码可读性
Java集合框架在不同JDK版本的演进
Java集合框架自JDK 1.2引入以来,随着JDK版本的迭代不断发展和优化。了解不同版本间的变化有助于我们更好地使用集合框架。
JDK 1.2 (1998) - 集合框架正式引入
- 引入基本的集合接口:
Collection、List、Set、Map - 提供主要实现类:
ArrayList、LinkedList、HashSet、HashMap等 - 引入
Iterator接口用于遍历集合
JDK 1.4 (2002)
- 添加
Collections.synchronizedMap/Set/List等同步包装器 - 引入
LinkedHashMap和LinkedHashSet
JDK 1.5 (2004) - 泛型引入
- 引入泛型支持,增强类型安全
- 添加
ConcurrentHashMap、CopyOnWriteArrayList等并发集合 - 引入
Queue接口及其实现 - 自动装箱和拆箱功能
JDK 1.6 (2006)
- 增强了
ConcurrentHashMap的性能 - 添加
Deque接口和ArrayDeque实现
JDK 1.7 (2011)
HashMap引入红黑树优化(链地址法优化)- 支持"钻石语法"简化泛型使用
- 增加
Collections.emptyIfNull等工具方法
JDK 1.8 (2014) - Lambda表达式
- 为集合接口添加默认方法:
forEach、removeIf、replaceAll等 - 引入
Stream API用于函数式数据处理 - 增加
Map接口新方法:forEach、compute、merge等 HashMap进一步优化,使用数组+链表+红黑树的结构
JDK 9 (2017) - 集合工厂方法
- 引入不可变集合工厂方法:
List.of()、Set.of()、Map.of() - 增强
Stream API功能
JDK 10 (2018)
List.copyOf()、Set.copyOf()、Map.copyOf()方法
JDK 11 (2018) 及以后
- 对现有集合实现进行性能优化
- 增强不可变集合的功能
- 改进垃圾回收对集合的处理
版本演进对比表
| JDK版本 | 主要变更 | 影响 |
|---|---|---|
| 1.2 | 引入基本框架 | 奠定基础 |
| 1.5 | 泛型、并发集合 | 提高类型安全和并发性能 |
| 1.8 | Lambda、Stream API | 函数式编程风格 |
| 9+ | 不可变集合工厂方法 | 更简洁创建不可变集合 |
小结
Java集合框架是Java编程中最重要的基础组件之一,掌握它的体系结构和核心接口是使用集合的前提。通过本章节的学习,我们了解了集合框架的整体结构、主要接口的设计理念和基本用法。在后续章节中,我们将深入学习各个接口的具体实现类,包括它们的底层原理、性能特征和最佳实践。
