Java 集合框架详解
Java 集合框架(Collection Framework)是 Java 中用来存储、管理和操作数据的一套标准化体系,就像现实生活中的 “容器”—— 不同的容器适合存放不同类型的数据,操作效率也各有侧重。它将复杂的数据结构(如数组、链表、哈希表等)封装成易用的类,开发者无需关注底层实现,只需调用现成的方法即可完成数据的增删改查。
一、集合框架的 “家族图谱”
Java 集合框架的核心是
Collection 和 Map 两大接口,它们之下衍生出众多实现类,覆盖了几乎所有常用的数据结构场景。整体结构可简化为:Java 集合框架
├─ Collection(单元素集合:存储单个值)
│ ├─ List(有序、可重复)
│ │ ├─ ArrayList(动态数组实现)
│ │ ├─ LinkedList(双向链表实现)
│ │ └─ Vector(线程安全的动态数组,古老类)
│ ├─ Set(无序、不可重复)
│ │ ├─ HashSet(哈希表实现)
│ │ ├─ TreeSet(红黑树实现,自动排序)
│ │ └─ LinkedHashSet(哈希表+链表,保留插入顺序)
│ └─ Queue(队列:先进先出FIFO)
│ ├─ LinkedList(链表实现,常用作队列)
│ └─ PriorityQueue(优先级队列,按元素大小排序)
└─ Map(键值对集合:存储“键-值”映射)
├─ HashMap(哈希表实现,无序)
├─ TreeMap(红黑树实现,键自动排序)
├─ LinkedHashMap(哈希表+链表,保留插入顺序)
└─ Hashtable(线程安全的哈希表,古老类)
二、Collection 接口:单元素集合的 “通用规则”
Collection 是所有单元素集合的根接口,定义了一套通用方法(如添加、删除、遍历元素),其子接口 List、Set、Queue 在此基础上扩展出各自的特性。1. List:有序可重复的 “动态数组”
List 集合的核心特点是 “有序”(元素有固定的索引位置,像数组一样按顺序存储)和 “可重复”(允许添加相同的元素)。(1)ArrayList:查询快、增删中间慢
-
底层结构:动态数组(长度可变的数组,当元素满时自动扩容为原来的 1.5 倍)。
-
核心优势:通过索引(如
get(0))访问元素极快(时间复杂度O(1)),适合频繁查询的场景。 -
核心劣势:在中间位置插入 / 删除元素时,需要移动后续元素(时间复杂度
O(n)),效率较低。 -
类比:像一本按页码排序的书,查第 100 页很容易,但在第 50 页插入一页,后面的页码都要重新编排。代码示例:
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { List<String> list = new ArrayList<>(); // 添加元素(有序,可重复) list.add("苹果"); list.add("香蕉"); list.add("苹果"); // 允许重复 // 按索引访问(0开始) System.out.println(list.get(1)); // 输出:香蕉 // 遍历(顺序与添加一致) for (String fruit : list) { System.out.println(fruit); // 苹果、香蕉、苹果 } } }
(2)LinkedList:增删中间快、查询慢
-
底层结构:双向链表(每个元素包含 “前驱” 和 “后继” 指针,像铁链一样串联)。
-
核心优势:在中间位置插入 / 删除元素时,只需修改前后元素的指针(时间复杂度
O(1)),适合频繁增删的场景。 -
核心劣势:查询元素时需从头遍历到目标位置(时间复杂度
O(n)),效率较低。 -
类比:像一串项链,拆中间的珠子只需解开两边的线,但要找第 100 颗珠子,必须从第一颗开始数。代码示例(特有方法):
import java.util.LinkedList; public class Test { public static void main(String[] args) { LinkedList<String> list = new LinkedList<>(); list.add("A"); list.add("B"); // 链表特有的首尾操作(效率极高) list.addFirst("头"); // 开头添加 list.addLast("尾"); // 结尾添加 System.out.println(list.getFirst()); // 头 System.out.println(list.getLast()); // 尾 } }
2. Set:无序不可重复的 “去重容器”
Set 集合的核心特点是 “无序”(元素没有固定位置,遍历顺序与添加顺序无关)和 “不可重复”(添加重复元素会被自动过滤)。(1)HashSet:去重快、无序
-
底层结构:哈希表(数组 + 链表 / 红黑树,通过元素的
hashCode()计算存储位置)。 -
核心优势:添加、删除、查询元素的效率极高(平均时间复杂度
O(1)),适合快速去重场景。 -
去重原理:依赖元素的
hashCode()和equals()方法 —— 两个元素若hashCode()不同,必不重复;若hashCode()相同,再通过equals()确认是否真的重复。 -
注意:存储自定义对象时,必须重写
hashCode()和equals(),否则无法正确去重。代码示例:import java.util.HashSet; import java.util.Set; public class Test { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("苹果"); set.add("香蕉"); set.add("苹果"); // 重复元素,自动过滤 // 遍历(顺序与添加顺序无关) for (String fruit : set) { System.out.println(fruit); // 苹果、香蕉(无重复) } } }
(2)TreeSet:去重 + 自动排序
-
底层结构:红黑树(一种自平衡的二叉排序树,元素会按大小自动排序)。
-
核心优势:自动按元素大小排序(如数字从小到大、字符串按字典序),同时去重,适合 “排序 + 去重” 场景(如排行榜)。
-
排序规则:元素需实现
Comparable接口(重写compareTo()方法),或创建TreeSet时传入Comparator比较器。代码示例:import java.util.TreeSet; public class Test { public static void main(String[] args) { TreeSet<Integer> set = new TreeSet<>(); set.add(3); set.add(1); set.add(2); set.add(3); // 重复元素,自动过滤 System.out.println(set); // 输出:[1, 2, 3](自动排序) } }
3. Queue:先进先出的 “排队容器”
Queue 集合遵循 “先进先出(FIFO)” 规则,就像现实中排队买票 —— 先到的人先处理。LinkedList:常用的队列实现
LinkedList 同时实现了 List 和 Queue 接口,因此常用作队列:import java.util.LinkedList;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
// 入队(添加到队尾)
queue.offer("第一个人");
queue.offer("第二个人");
// 出队(移除并返回队头)
System.out.println(queue.poll()); // 第一个人(先入先出)
System.out.println(queue.poll()); // 第二个人
}
}
PriorityQueue:优先级队列(不遵循 FIFO)
元素按 “优先级” 排序,每次出队的是优先级最高的元素(如数字默认按从小到大):
import java.util.PriorityQueue;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(1);
pq.offer(2);
System.out.println(pq.poll()); // 1(优先级最高)
System.out.println(pq.poll()); // 2
}
}
三、Map 接口:键值对集合的 “字典容器”
Map 集合存储的是 “键(Key)- 值(Value)” 对,就像字典中 “单词 - 解释” 的对应关系 —— 通过 “键” 可以快速找到对应的 “值”。- 键(Key):唯一不可重复(类似
Set的元素); - 值(Value):可以重复;
- 一个键只能对应一个值(若添加相同的键,会覆盖旧值)。
1. HashMap:键值对存储的 “效率之王”
-
底层结构:哈希表(JDK1.8 后为 “数组 + 链表 + 红黑树”—— 当链表长度超过 8 时转为红黑树,优化查询效率)。
-
核心优势:根据键查询值的效率极高(平均时间复杂度
O(1)),适合频繁的 “存、取” 操作。 -
注意:键的去重逻辑与
HashSet一致,依赖hashCode()和equals()方法。代码示例:import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { Map<String, Integer> scoreMap = new HashMap<>(); // 存键值对 scoreMap.put("张三", 90); scoreMap.put("李四", 85); scoreMap.put("张三", 95); // 键重复,覆盖旧值 // 查值(通过键) System.out.println(scoreMap.get("李四")); // 85 // 遍历所有键值对 for (Map.Entry<String, Integer> entry : scoreMap.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); // 输出:张三:95、李四:85(顺序不确定) } } }
2. TreeMap:键自动排序的 “有序字典”
-
底层结构:红黑树,键会按大小自动排序(如数字从小到大、字符串按字典序)。
-
适合场景:需要按键排序的场景(如按学号排序的成绩表、按日期排序的日志)。代码示例:
import java.util.TreeMap; public class Test { public static void main(String[] args) { TreeMap<Integer, String> studentMap = new TreeMap<>(); studentMap.put(3, "张三"); studentMap.put(1, "李四"); studentMap.put(2, "王五"); // 遍历(键自动排序) for (Map.Entry<Integer, String> entry : studentMap.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); // 输出:1:李四、2:王五、3:张三 } } }
四、集合框架的 “通用工具”
1. 迭代器(Iterator):遍历集合的统一方式
所有
Collection 的实现类都支持通过 Iterator 遍历元素,避免暴露底层结构:import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
// 获取迭代器
Iterator<String> it = list.iterator();
// 遍历
while (it.hasNext()) { // 判断是否有下一个元素
String s = it.next(); // 获取下一个元素
System.out.println(s);
}
}
}
注意:遍历过程中若要删除元素,必须用迭代器的
remove() 方法,否则会抛出 ConcurrentModificationException(快速失败机制)。2. Collections 工具类:集合操作的 “辅助工具”
java.util.Collections 提供了大量静态方法,简化集合操作:sort():排序(如Collections.sort(list));reverse():反转元素顺序;shuffle():随机打乱元素;synchronizedList():将非线程安全的集合转为线程安全(如Collections.synchronizedList(new ArrayList<>()))。
五、如何选择合适的集合?
记住核心场景,按 “需求” 选工具:
| 需求场景 | 推荐集合 |
|---|---|
| 有序可重复,频繁查询 | ArrayList |
| 有序可重复,频繁增删中间元素 | LinkedList |
| 无序不可重复,追求效率 | HashSet |
| 去重且需要排序 | TreeSet |
| 键值对存储,无序,追求效率 | HashMap |
| 键值对存储,需要按键排序 | TreeMap |
| 先进先出(排队) | LinkedList(作为 Queue) |
| 按优先级处理元素 | PriorityQueue |
六、避坑指南
-
集合与数组的区别:
数组长度固定,集合长度动态可变;数组可存储基本类型(如int[]),集合只能存储对象(基本类型需装箱为Integer等)。 -
线程安全性:
常用集合(ArrayList、HashMap、HashSet)均为非线程安全,多线程环境下需使用ConcurrentHashMap(线程安全的 Map)、CopyOnWriteArrayList(线程安全的 List)等。 -
自定义对象作为键 / 元素:
存储自定义对象到HashSet或作为HashMap的键时,必须重写hashCode()和equals()方法,否则无法正确去重或查询
浙公网安备 33010602011771号