【Java从入门到入土】20:集合框架入门:为什么需要这么多容器?
【Java从入门到入土】20:集合框架入门:为什么需要这么多容器?
Java集合框架(Collection Framework)是处理“一组数据”的核心工具——从简单的ArrayList到高性能的HashMap,从线程安全的Vector到并发友好的ConcurrentHashMap,Java提供了数十种集合类型。新手常困惑:“为什么需要这么多容器?直接用数组不行吗?” 答案是:不同场景对数据的“存取、遍历、排序、并发”需求不同,集合框架通过分工明确的容器类型,让开发者能精准匹配业务场景。今天从集合框架的整体架构,到Collection与Map的核心区别,再到遍历接口、线程安全、选型策略,把集合框架的“分工”与“选择”逻辑讲透,让你不再盲目使用ArrayList和HashMap。
🗺️ 集合框架全景图:一张图看懂所有关系
Java集合框架并非零散的类,而是一套有清晰继承/实现关系的体系——核心分为Collection(单列集合)和Map(双列集合)两大阵营,所有集合类都直接/间接实现自Iterable(可遍历)或Map接口,且大部分位于java.util包下。
1. 核心体系结构图
2. 核心设计原则
- 接口化:通过
List/Set/Map等接口定义行为,具体实现类(如ArrayList/HashMap)负责底层逻辑; - 解耦化:遍历逻辑抽离为
Iterable/Iterator,所有集合的遍历方式统一; - 差异化:不同实现类适配不同场景(如
ArrayList查快改慢,LinkedList改快查慢); - 扩展化:支持自定义集合(实现
Collection/Map接口),或通过工具类(Collections)扩展功能。
3. 集合vs数组:核心优势
| 维度 | 数组 | 集合框架 |
|---|---|---|
| 长度灵活性 | 固定长度,无法动态扩容 | 动态扩容(如ArrayList自动扩容) |
| 数据类型 | 仅支持基本类型/引用类型,单一类型 | 支持泛型,类型安全,可存任意引用类型 |
| 功能丰富度 | 仅支持遍历,无增删改查方法 | 内置add/remove/contains等方法 |
| 排序/查找 | 需手动实现排序/查找逻辑 | 内置sort/contains/keySet等方法 |
| 线程安全 | 无线程安全支持 | 提供线程安全实现(如Vector、Hashtable) |
⚔️ Collection与Map两大阵营
集合框架的核心是“单列”和“双列”的分工:Collection处理“一组独立元素”(如订单列表),Map处理“键值对映射”(如用户ID→用户信息),二者是完全不同的设计目标。
1. Collection:单列集合(一组元素)
Collection是所有单列集合的根接口,定义了添加、删除、遍历、判断元素的核心方法,分为三大子接口:
| 子接口 | 核心特征 | 典型实现类 | 适用场景 |
|---|---|---|---|
List | 有序、可重复、有索引 | ArrayList、LinkedList | 需按顺序存取(如订单列表、商品列表) |
Set | 无序、不可重复、无索引 | HashSet、TreeSet | 需去重(如用户标签、唯一ID集合) |
Queue | 有序(FIFO)、可重复 | LinkedList、PriorityQueue | 需先进先出(如任务队列、消息队列) |
核心用法示例
import java.util.*;
public class CollectionTest {
public static void main(String[] args) {
// ========== List:有序可重复 ==========
List<String> list = new ArrayList<>();
list.add("Java");
list.add("集合");
list.add("Java"); // 允许重复
System.out.println("List:" + list); // [Java, 集合, Java]
System.out.println("索引1的元素:" + list.get(1)); // 集合(有序、有索引)
// ========== Set:无序不可重复 ==========
Set<String> set = new HashSet<>();
set.add("Java");
set.add("集合");
set.add("Java"); // 自动去重
System.out.println("Set:" + set); // [Java, 集合](无序)
// set.get(0); // 编译报错:无索引方法
// ========== Queue:FIFO ==========
Queue<String> queue = new LinkedList<>();
queue.offer("任务1");
queue.offer("任务2");
System.out.println("队列头:" + queue.peek()); // 任务1
System.out.println("取出队列头:" + queue.poll()); // 任务1
System.out.println("剩余队列:" + queue); // [任务2]
}
}
2. Map:双列集合(键值对)
Map是所有双列集合的根接口,存储“键(Key)-值(Value)”映射关系,核心特征是键唯一、值可重复,通过键快速查找值(哈希表实现的HashMap查找效率接近O(1))。
核心用法示例
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
// 1. 创建Map(键:用户ID,值:用户名)
Map<Long, String> userMap = new HashMap<>();
userMap.put(1L, "张三");
userMap.put(2L, "李四");
userMap.put(1L, "张三三"); // 键重复,覆盖值
System.out.println("Map:" + userMap); // {1=张三三, 2=李四}
// 2. 核心操作
System.out.println("获取键1的值:" + userMap.get(1L)); // 张三三
System.out.println("是否包含键2:" + userMap.containsKey(2L)); // true
System.out.println("是否包含值李四:" + userMap.containsValue("李四")); // true
// 3. 遍历Map
// 方式1:遍历键集
Set<Long> keySet = userMap.keySet();
for (Long key : keySet) {
System.out.println("键:" + key + ",值:" + userMap.get(key));
}
// 方式2:遍历键值对(推荐)
Set<Map.Entry<Long, String>> entrySet = userMap.entrySet();
for (Map.Entry<Long, String> entry : entrySet) {
System.out.println("键:" + entry.getKey() + ",值:" + entry.getValue());
}
}
}
3. Collection与Map的核心区别
| 维度 | Collection(单列) | Map(双列) |
|---|---|---|
| 存储形式 | 单个元素(如String、User) | 键值对(Key-Value) |
| 核心特征 | List有序可重复,Set无序不可重复 | 键唯一,值可重复 |
| 查找方式 | 遍历查找(List可索引,Set需遍历) | 键查找(哈希实现O(1),树实现O(logn)) |
| 核心接口方法 | add()、remove()、contains() | put()、get()、containsKey() |
| 遍历依赖 | 直接实现Iterable接口 | 需转成Set(keySet/entrySet)遍历 |
🔄 Iterable与Iterator:遍历的统一接口
遍历是集合最常用的操作,Java集合框架通过Iterable(可遍历)和Iterator(迭代器)接口,为所有Collection集合提供了统一的遍历方式,避免每种集合都实现一套遍历逻辑。
1. 核心接口关系
Iterable:所有Collection集合的父接口,定义了iterator()方法,返回Iterator对象;Iterator:迭代器接口,定义了hasNext()(是否有下一个元素)、next()(获取下一个元素)、remove()(删除当前元素)方法;- 增强for循环(
for-each):编译器语法糖,底层自动调用Iterable.iterator()。
2. 迭代器的核心用法
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("集合");
list.add("迭代器");
// ========== 方式1:Iterator迭代器(最基础) ==========
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println("迭代器遍历:" + element);
// 遍历中删除元素(安全方式)
if (element.equals("集合")) {
iterator.remove(); // 避免ConcurrentModificationException
}
}
System.out.println("删除后List:" + list); // [Java, 迭代器]
// ========== 方式2:增强for循环(语法糖) ==========
for (String element : list) {
System.out.println("增强for遍历:" + element);
// 遍历中不能用list.remove(),会抛ConcurrentModificationException
}
// ========== 方式3:forEach方法(Java 8+,函数式) ==========
list.forEach(element -> System.out.println("forEach遍历:" + element));
}
}
3. 迭代器的核心优势
- 统一遍历:无论
ArrayList、HashSet、LinkedList,都能用Iterator遍历; - 安全删除:迭代器的
remove()方法可在遍历中安全删除元素,避免ConcurrentModificationException; - 解耦遍历逻辑:集合只需实现
Iterable接口,无需关心遍历细节,迭代器负责具体遍历逻辑。
4. 常见坑点:遍历中修改集合
// 反例:增强for循环中删除元素,抛ConcurrentModificationException
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 错误写法
/*
for (String s : list) {
if (s.equals("B")) {
list.remove(s); // 抛异常
}
}
*/
// 正确写法:使用Iterator
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("B")) {
iterator.remove(); // 安全删除
}
}
System.out.println(list); // [A, C]
🔒 集合的线程安全问题初步探讨
新手常忽略集合的线程安全——大部分集合(如ArrayList、HashMap)是非线程安全的,多线程并发修改会导致数据错乱或ConcurrentModificationException。
1. 线程安全问题示例
import java.util.ArrayList;
import java.util.List;
// 多线程修改ArrayList,导致数据错乱
public class CollectionThreadSafeTest {
private static final List<String> list = new ArrayList<>();
public static void main(String[] args) throws InterruptedException {
// 10个线程,每个线程添加1000个元素
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
list.add(Thread.currentThread().getName() + "-" + j);
}
}).start();
}
// 等待所有线程执行完毕
Thread.sleep(2000);
// 预期10000个元素,实际可能少于10000,甚至抛异常
System.out.println("集合大小:" + list.size());
}
}
运行结果:
// 可能输出:集合大小:9876(数据丢失)
// 或抛:ConcurrentModificationException
2. 线程安全集合的分类
| 类型 | 线程安全实现方式 | 典型集合 | 性能/适用场景 |
|---|---|---|---|
| 传统安全集合 | synchronized同步方法 | Vector、Hashtable、Stack | 性能低,适用于并发量小的场景 |
| 工具类包装 | Collections.synchronizedXxx() | synchronizedList、synchronizedMap | 性能一般,按需包装非安全集合 |
| 并发集合 | CAS+分段锁/无锁算法 | ConcurrentHashMap、CopyOnWriteArrayList | 性能高,适用于高并发场景 |
3. 线程安全集合使用示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ThreadSafeCollectionTest {
public static void main(String[] args) {
// 方式1:Collections包装(性能一般)
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 方式2:并发集合(性能高,推荐)
List<String> cowList = new CopyOnWriteArrayList<>();
// 多线程操作cowList(安全)
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
cowList.add(Thread.currentThread().getName() + "-" + j);
}
}).start();
}
// 等待执行
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("CopyOnWriteArrayList大小:" + cowList.size()); // 5000(准确)
}
}
4. 核心结论
- 非并发场景:优先用
ArrayList、HashMap(性能高); - 低并发场景:用
Collections.synchronizedXxx()包装; - 高并发场景:用
java.util.concurrent包下的并发集合(如ConcurrentHashMap、CopyOnWriteArrayList); - 避免使用:
Vector、Hashtable(老旧实现,性能差)。
🤔 如何选择合适的集合类型:决策流程图
选择集合的核心是“匹配业务场景”——先明确需求(有序/无序、去重/重复、查找/修改、并发),再按流程选择:
1. 核心决策流程图
2. 场景化选型指南
| 业务场景 | 推荐集合类型 | 核心原因 |
|---|---|---|
| 商品列表(频繁查询,偶尔增删) | ArrayList | 动态数组,索引查询O(1) |
| 消息队列(频繁增删,首尾操作) | LinkedList/ArrayDeque | 双向链表,首尾增删O(1) |
| 用户标签(去重,无序) | HashSet | 哈希实现,去重+查找O(1) |
| 历史记录(有序,去重,插入顺序) | LinkedHashSet | 保留插入顺序,去重 |
| 排行榜(有序,排序) | TreeSet | 红黑树实现,自动排序 |
| 用户信息映射(键值对,无序) | HashMap | 哈希实现,查找O(1),性能高 |
| 配置文件(键值对,插入顺序) | LinkedHashMap | 保留插入顺序,适配配置文件 |
| 字典排序(键值对,排序) | TreeMap | 红黑树实现,键自动排序 |
| 高并发缓存(键值对,线程安全) | ConcurrentHashMap | 分段锁,高并发下性能优于Hashtable |
3. 选型核心原则
- 优先匹配性能:查询多用
ArrayList/HashMap,增删多用LinkedList; - 去重选Set:普通去重选
HashSet,有序去重选LinkedHashSet,排序去重选TreeSet; - 键值对选Map:无序选
HashMap,有序选TreeMap/LinkedHashMap,并发选ConcurrentHashMap; - 并发场景慎选:非并发场景不用线程安全集合(性能损耗),高并发不用
Vector/Hashtable; - 简单优先:无特殊需求时,单列用
ArrayList,双列用HashMap(最常用、最易理解)。
📌 核心总结
Java集合框架的设计核心是“分工适配场景”,关键要点如下:
- 集合框架分为
Collection(单列)和Map(双列)两大阵营,Collection下分List(有序可重复)、Set(无序不可重复)、Queue(队列); Iterable/Iterator为所有Collection提供统一遍历方式,迭代器的remove()是遍历中删除元素的安全方式;- 大部分集合(
ArrayList/HashMap)非线程安全,并发场景需用ConcurrentHashMap/CopyOnWriteArrayList; - 集合选型遵循“场景匹配”原则:查询多用数组实现(
ArrayList/HashMap),增删多用链表实现(LinkedList),去重选Set,排序选TreeSet/TreeMap; - 避免过度设计:无特殊需求时,优先用
ArrayList(单列)和HashMap(双列)。
使用集合框架的核心原则:先明确业务需求(有序/去重/并发/性能),再按决策流程选容器,而非盲目使用最熟悉的类型——掌握这些,你就能在不同场景下选择最合适的集合,让代码既高效又易维护。
更多推荐
所有评论(0)