Java集合框架概述 ------ Collection与Map体系全解析
文章目录
- [Java集合框架概述 ------ Collection与Map体系全解析](#Java集合框架概述 —— Collection与Map体系全解析)
-
- 前言
- 一、Java集合框架的体系结构
- 二、Collection接口详解
-
- [2.1 Collection核心方法](#2.1 Collection核心方法)
- [2.2 List接口](#2.2 List接口)
- [2.3 Set接口](#2.3 Set接口)
- [2.4 Queue接口](#2.4 Queue接口)
- 三、Map接口详解
-
- [3.1 Map核心方法](#3.1 Map核心方法)
- [3.2 HashMap使用示例](#3.2 HashMap使用示例)
- 四、迭代器详解
-
- [4.1 迭代器的三个核心方法](#4.1 迭代器的三个核心方法)
- [4.2 迭代过程中安全删除元素](#4.2 迭代过程中安全删除元素)
- [4.3 增强for循环的本质](#4.3 增强for循环的本质)
- 五、迭代器模式的设计思想
- 六、集合的选择策略
- 总结
- [✅ 亮点总结](#✅ 亮点总结)
- 适用场景
- 扩展方向
前言
在Java开发中,数据的存储与操作是永恒的核心话题。无论是处理用户列表、缓存数据,还是构建复杂的数据结构,都离不开集合框架的支持。Java集合框架(Java Collections Framework,简称JCF)是JDK提供的一套设计精良的数据结构工具库,理解它的体系结构对于写出高效、优雅的Java代码至关重要。
集合框架的核心设计思想是接口与实现分离 ------所有的集合操作都通过接口(如List、Set、Map)来定义规范,不同的实现类(如ArrayList、LinkedList、HashMap)提供不同的底层数据结构和性能特征。这种设计让你可以在不修改调用代码的情况下切换实现类:比如从ArrayList换成LinkedList,只需修改一行new语句,其余代码保持不变。这种灵活性正是面向接口编程的最佳体现。
本文将带你从整体上把握Java集合框架的体系结构,理解Collection和Map两大接口体系,并掌握迭代器的使用方式。这篇文章是后续ArrayList/LinkedList对比和HashMap深度解析两篇文章的基础铺垫。
一、Java集合框架的体系结构
Java集合框架主要分为两大接口体系:Collection 和Map 。这个划分非常清晰------Collection是"单列数据"的抽象(一条一条的元素),Map是"键值对数据"的抽象(Key→Value的映射关系)。两者是并列关系而非继承关系------Map不是Collection的子接口,这一点在面试中经常被用来制造陷阱。
- Collection接口:存储单个元素,是List、Set、Queue的父接口
- Map接口:存储键值对(Key-Value),与Collection接口是并列关系
整体继承关系如下:
Collection (接口)
├── List (接口) ------ 有序、可重复
│ ├── ArrayList
│ ├── LinkedList
│ └── Vector (已基本淘汰)
├── Set (接口) ------ 无序、不可重复
│ ├── HashSet
│ ├── LinkedHashSet
│ └── TreeSet
└── Queue/Deque (接口) ------ 队列/双端队列
├── LinkedList
├── ArrayDeque
└── PriorityQueue
Map (接口) ------ 键值对
├── HashMap
├── LinkedHashMap
├── TreeMap
└── Hashtable (已基本淘汰)
下面通过一个简单的示例来感受不同集合的特点:
java
import java.util.*;
public class CollectionOverview {
public static void main(String[] args) {
// List:有序可重复
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Java"); // 允许重复
System.out.println("List: " + list); // [Java, Python, Java]
// Set:无序不可重复
Set<String> set = new HashSet<>();
set.add("Java");
set.add("Python");
set.add("Java"); // 重复元素不会添加
System.out.println("Set: " + set); // [Java, Python] 或 [Python, Java]
// Map:键值对
Map<String, Integer> map = new HashMap<>();
map.put("Java", 1995);
map.put("Python", 1991);
map.put("Java", 1996); // 覆盖旧值
System.out.println("Map: " + map); // {Java=1996, Python=1991}
}
}
二、Collection接口详解
Collection是List、Set、Queue的最高父接口,定义了集合操作的通用方法。
2.1 Collection核心方法
java
public interface Collection<E> extends Iterable<E> {
int size(); // 返回元素个数
boolean isEmpty(); // 判断是否为空
boolean contains(Object o); // 是否包含某元素
boolean add(E e); // 添加元素
boolean remove(Object o); // 移除元素
void clear(); // 清空集合
Iterator<E> iterator(); // 获取迭代器
Object[] toArray(); // 转换为数组
// ... 还有其他方法
}
2.2 List接口
List是有序集合,可以通过索引访问元素。核心特点:有序、可重复、有索引。
java
public class ListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 索引访问
System.out.println(list.get(1)); // B
// 在指定位置插入
list.add(1, "X");
System.out.println(list); // [A, X, B, C]
// 遍历方式
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " "); // A X B C
}
}
}
2.3 Set接口
Set 不允许重复元素,主要用于去重场景。判断重复依赖**equals()和hashCode()**方法。
java
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("apple"); // 不会添加
System.out.println(set.size()); // 2
// TreeSet 实现排序
Set<Integer> sortedSet = new TreeSet<>();
sortedSet.add(5);
sortedSet.add(1);
sortedSet.add(3);
System.out.println(sortedSet); // [1, 3, 5] 自动排序
}
}
2.4 Queue接口
Queue 模拟队列数据结构,遵循FIFO(先进先出)原则。在Java中,Queue是一个接口,LinkedList是它最常用的实现(同时实现了List和Deque)。理解Queue的API设计模式很重要,因为它体现了Java集合框架的一个设计惯例------用不同返回值处理不同错误场景:
| 操作 | 抛出异常 | 返回特殊值 |
|---|---|---|
| 入队 | add(e) |
offer(e) → false |
| 出队 | remove() |
poll() → null |
| 查看队首 | element() |
peek() → null |
这种"双API"设计让你可以根据场景选择:如果队列为空是你预料之内的情况,用poll()返回null更优雅;如果队列应该永远非空,用remove()让异常暴露问题更合适。
java
public class QueueDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.offer("A"); // 入队
queue.offer("B");
queue.offer("C");
System.out.println(queue.poll()); // A 出队并移除
System.out.println(queue.peek()); // B 查看队首不移除
System.out.println(queue); // [B, C]
}
}
三、Map接口详解
Map存储键值对(Key-Value),每个Key最多映射一个Value。
3.1 Map核心方法
java
public interface Map<K, V> {
V put(K key, V value); // 添加键值对
V get(Object key); // 根据Key获取Value
V remove(Object key); // 根据Key移除
boolean containsKey(Object key); // 是否包含Key
boolean containsValue(Object value); // 是否包含Value
Set<K> keySet(); // 获取所有Key的Set集合
Collection<V> values(); // 获取所有Value的Collection
Set<Map.Entry<K, V>> entrySet(); // 获取所有键值对
// ...
}
3.2 HashMap使用示例
java
public class MapDemo {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 85);
map.put("李四", 92);
map.put("王五", 78);
// 遍历方式1:通过keySet
for (String key : map.keySet()) {
System.out.println(key + " -> " + map.get(key));
}
// 遍历方式2:通过entrySet(推荐,效率更高)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// 遍历方式3:forEach(Java 8+)
map.forEach((key, value) ->
System.out.println(key + " -> " + value));
}
}
四、迭代器详解
Iterator 是遍历集合的统一方式,所有Collection的子类都可以通过iterator()获取迭代器。
4.1 迭代器的三个核心方法
java
public class IteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
}
}
- hasNext():判断是否还有下一个元素
- next():返回当前元素并将指针后移
- remove():移除上次next()返回的元素
4.2 迭代过程中安全删除元素
遍历集合时,如果直接用集合的remove()方法删除元素,会抛出ConcurrentModificationException 。这是因为集合内部有一个modCount计数器,每次结构性修改都会自增,而迭代器在创建时会记录当时的modCount值,每次next()时检查是否一致。如果直接用集合的remove()(会修改modCount),迭代器的检查就会失败------这是一种**fail-fast(快速失败)**机制,用于尽早暴露并发修改问题。
正确做法是使用迭代器的remove()方法,它会同步更新自己的expectedModCount。JDK 8还提供了更简洁的removeIf()方法,内部也是基于迭代器实现。
java
public class SafeRemoveDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
// 错误做法:会抛出ConcurrentModificationException
// for (String s : list) {
// if ("Python".equals(s)) {
// list.remove(s); // 危险!
// }
// }
// 正确做法:使用迭代器的remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("Python".equals(s)) {
it.remove(); // 安全删除
}
}
System.out.println(list); // [Java, C++]
}
}
4.3 增强for循环的本质
增强for循环(foreach)实际上是迭代器的语法糖,编译后等价于使用Iterator遍历:
java
// 源码写法
for (String s : list) {
System.out.println(s);
}
// 编译后等价于
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
五、迭代器模式的设计思想
迭代器模式的精髓在于将遍历行为从集合本身分离出来。这样做的好处是:
- 解耦:遍历算法与集合的实现分离
- 统一接口:无论底层是数组还是链表,遍历方式都一样
- 封装内部结构:调用者无需知道集合的内部实现
设计模式实践
java
// 使用ListIterator实现双向遍历(仅List实现支持)
public class ListIteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("一");
list.add("二");
list.add("三");
ListIterator<String> listIterator = list.listIterator();
// 正向遍历
while (listIterator.hasNext()) {
System.out.print(listIterator.next() + " "); // 一 二 三
}
System.out.println();
// 反向遍历
while (listIterator.hasPrevious()) {
System.out.print(listIterator.previous() + " "); // 三 二 一
}
}
}
六、集合的选择策略
在实际开发中,选择合适的集合类型非常重要,这直接影响程序的性能和可读性。以下是一些选择建议:
| 需求场景 | 推荐集合 |
|---|---|
| 需要快速随机访问 | ArrayList |
| 频繁插入删除 | LinkedList |
| 需要去重 | HashSet |
| 需要排序 | TreeSet / TreeMap |
| 需要键值对 | HashMap |
| 线程安全场景 | ConcurrentHashMap / CopyOnWriteArrayList |
| 按插入顺序遍历 | LinkedHashMap / LinkedHashSet |
总结
本文介绍了Java集合框架的整体架构,涵盖了Collection和Map两大接口体系,以及迭代器的使用方式。集合框架是Java中最常用的工具库,可以说任何Java项目都离不开它。以下是本文的核心收获:
- Collection体系:List(有序可重复,适合按索引操作)、Set(无序不可重复,适合去重)、Queue(FIFO队列,适合任务调度)三大子接口各有专攻
- Map体系:键值对存储,HashMap是最常用的实现,TreeMap适合排序场景,LinkedHashMap适合按插入顺序遍历
- Iterator迭代器:统一的遍历接口,将遍历逻辑与底层数据结构解耦。增强for循环本质是迭代器的语法糖
- fail-fast机制 :
ConcurrentModificationException是集合的"安全阀",防止在遍历过程中被意外修改导致数据不一致
掌握这些基础知识后,在后续文章中我们将深入探讨ArrayList和LinkedList的底层原理、HashMap的哈希表实现、以及多线程环境下的并发集合等高级话题。集合框架的真正功力体现在选型能力上------同样是存储100万条数据,选ArrayList还是LinkedList?选HashMap还是TreeMap?这取决于你对底层数据结构的理解深度。
Java集合框架的设计充分体现了接口与实现分离、抽象层次分明的设计思想。理解这套框架,对于一个Java开发者来说不仅是面试的必备知识,更是写出高质量代码的基础。
✅ 亮点总结
- Collection与Map两大体系划分:Collection存储单列数据(List/Set/Queue),Map存储键值对(HashMap/TreeMap),用一张全景图理清所有集合类的归属
- Iterator迭代器模式的优雅设计 :将遍历行为与底层数据结构解耦,无论
ArrayList数组还是LinkedList链表,hasNext()/next()接口完全统一 - ListIterator的双向遍历能力 :相比普通Iterator只能"前进",ListIterator支持"后退"(
hasPrevious()/previous()),适合需要双向操作的场景 - 集合选择速查表:一张表格将"需随机访问→ArrayList"、"需去重→HashSet"、"需排序→TreeMap"等常见场景一一对应,减少选型犹豫
- 接口与实现分离的设计思想 :
List<String> list = new ArrayList<>()------声明用接口,实现选具体类,这是面向接口编程的经典实践
适用场景
- 全局搜索项目中所有使用
List的地方,检查是否选择了正确的实现类(是否滥用LinkedList或漏用HashSet去重) - 处理从数据库或API返回的批量数据时,根据后续操作(遍历/随机访问/去重/排序)选择合适的集合类型
- 重构代码时,将
for (int i = 0; i < list.size(); i++)统一替换为增强for循环或迭代器,提升代码可读性
扩展方向
- ArrayList vs LinkedList深度对比 :理解两者底层数据结构和时间复杂度差异,做出最优选型,推荐阅读 12_ArrayList与LinkedList深度对比
- HashMap底层原理 :数组+链表+红黑树的哈希表实现,JDK 1.7到1.8的演进故事,推荐阅读 13_HashMap底层原理详解
- 并发集合 :
ConcurrentHashMap的分段锁机制、CopyOnWriteArrayList的写时复制策略,理解多线程环境下的集合安全方案