开篇概览:Java 集合框架整体架构
Java 集合框架(Java Collections Framework, JCF)是 Java 标准库中用于存储和操作一组对象的核心工具集。它提供了一套高性能、高复用、类型安全的接口与实现类,极大简化了数据结构的使用。
集合框架主要分为两大体系:
-
Collection接口体系:用于存储单个元素的集合;List:有序、可重复(如ArrayList,LinkedList);Set:无序、不可重复(如HashSet,TreeSet);Queue:队列(本章暂不展开)。
-
Map接口体系:用于存储键值对(Key-Value);- 如
HashMap,TreeMap,LinkedHashMap。
- 如
此外,还包括:
- 迭代器(Iterator):统一遍历集合;
- 工具类(Collections):提供排序、查找、同步等静态方法。
掌握集合框架,不仅能高效处理数据,还能理解其底层数据结构(数组、链表、哈希表、红黑树等)的设计思想。
一、Collection 接口体系
1.1 Collection 接口核心方法
所有集合类(除 Map)都实现 java.util.Collection 接口,提供统一操作:
boolean add(E e); // 添加元素
boolean remove(Object o); // 删除元素
int size(); // 元素个数
boolean isEmpty(); // 是否为空
boolean contains(Object o); // 是否包含某元素
Iterator<E> iterator(); // 获取迭代器
void clear(); // 清空集合
二、List 接口及其实现
List 是有序、可重复的集合,支持通过索引访问元素。
2.1 ArrayList(基于动态数组)
✅ 特点:
- 底层结构:
Object[]数组; - 查询快:
O(1)(通过索引直接访问); - 增删慢:
O(n)(需移动后续元素); - 线程不安全;
- 自动扩容:默认初始容量 10,扩容为原容量的 1.5 倍。
📌 底层原理详解:
- 当添加元素超过当前数组容量时,调用
grow()方法:int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍 - 使用
System.arraycopy()复制旧数组到新数组。
示例:ArrayList 使用
import java.util.*;
public class ArrayListDemo {
public static void main(String[] args) {
// 创建 ArrayList(初始容量默认为10)
List<String> list = new ArrayList<>();
// 添加元素(自动扩容)
list.add("苹果");
list.add("香蕉");
list.add("橙子");
System.out.println("列表内容: " + list); // [苹果, 香蕉, 橙子]
// 通过索引访问
System.out.println("第一个元素: " + list.get(0)); // 苹果
// 在指定位置插入(后续元素后移)
list.add(1, "葡萄");
System.out.println("插入后: " + list); // [苹果, 葡萄, 香蕉, 橙子]
// 删除元素(后续元素前移)
list.remove("香蕉");
System.out.println("删除后: " + list); // [苹果, 葡萄, 橙子]
// 遍历(推荐使用增强 for 或 Iterator)
for (String fruit : list) {
System.out.println("水果: " + fruit);
}
}
}
2.2 LinkedList(基于双向链表)
✅ 特点:
- 底层结构:双向链表(每个节点包含
prev、next、item); - 增删快:
O(1)(只需修改指针); - 查询慢:
O(n)(需从头/尾遍历); - 线程不安全;
- 额外方法:
addFirst()、addLast()、getFirst()等(实现Deque接口)。
📌 底层原理详解:
- 节点类定义:
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } - 插入时只需调整前后节点指针,无需移动数据。
示例:LinkedList 使用
import java.util.*;
public class LinkedListDemo {
public static void main(String[] args) {
// 创建 LinkedList
List<String> list = new LinkedList<>();
// 添加元素
list.add("A");
list.add("B");
list.add("C");
// 使用 LinkedList 特有方法(需强转或声明为 LinkedList)
LinkedList<String> linkedList = (LinkedList<String>) list;
linkedList.addFirst("Head"); // 在头部添加
linkedList.addLast("Tail"); // 在尾部添加
System.out.println("链表内容: " + linkedList); // [Head, A, B, C, Tail]
// 删除首尾元素
System.out.println("移除头部: " + linkedList.removeFirst()); // Head
System.out.println("移除尾部: " + linkedList.removeLast()); // Tail
// 遍历
for (String s : linkedList) {
System.out.println("元素: " + s);
}
}
}
✅ ArrayList vs LinkedList 选择建议:
| 场景 | 推荐 |
|---|---|
| 频繁随机访问(get/set) | ArrayList |
| 频繁在首尾增删 | LinkedList |
| 内存敏感(LinkedList 节点多占内存) | ArrayList |
三、Set 接口及其实现
Set 是无序、不可重复的集合(基于 equals() 判断重复)。
3.1 HashSet(基于哈希表)
✅ 特点:
- 底层结构:
HashMap(JDK 8+ 为 数组 + 链表/红黑树); - 无序:元素顺序与插入顺序无关;
- 查找/添加/删除:平均
O(1); - 允许一个
null元素; - 线程不安全。
📌 底层原理详解(JDK 8+):
- 使用
HashMap<E, Object>存储,key为元素,value为固定PRESENT对象; - 哈希冲突时:
- 链表长度 < 8:使用链表;
- 链表长度 ≥ 8 且数组长度 ≥ 64:转为红黑树(提升查找效率);
- 扩容机制:当元素数量 >
容量 * 负载因子(默认 0.75)时扩容为 2 倍。
示例:HashSet 使用
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
// 创建 HashSet
Set<String> set = new HashSet<>();
// 添加元素(自动去重)
set.add("Java");
set.add("Python");
set.add("Java"); // 重复,不会添加
set.add(null); // 允许一个 null
System.out.println("集合大小: " + set.size()); // 3
System.out.println("是否包含 Java: " + set.contains("Java")); // true
// 遍历(顺序不确定)
for (String lang : set) {
System.out.println("编程语言: " + lang);
}
// 删除元素
set.remove("Python");
System.out.println("删除后: " + set);
}
}
3.2 LinkedHashSet(基于 LinkedHashMap)
✅ 特点:
- 底层结构:
LinkedHashMap(哈希表 + 双向链表); - 有序:按插入顺序迭代;
- 性能略低于 HashSet(因维护链表);
- 其他特性同 HashSet。
示例:LinkedHashSet 使用
import java.util.*;
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<>();
set.add("第三");
set.add("第一");
set.add("第二");
set.add("第一"); // 重复,忽略
// 按插入顺序输出
System.out.println("LinkedHashSet 顺序: " + set); // [第三, 第一, 第二]
}
}
3.3 TreeSet(基于红黑树)
✅ 特点:
- 底层结构:红黑树(自平衡二叉搜索树);
- 有序:按自然顺序或自定义比较器排序;
- 查找/添加/删除:
O(log n); - 不允许
null(除非自定义比较器处理); - 线程不安全。
📌 红黑树特性:
- 每个节点是红色或黑色;
- 根节点是黑色;
- 红色节点的子节点必须是黑色;
- 从任一节点到其每个叶子的所有路径包含相同数目的黑色节点;
- 保证树高度为
O(log n),操作高效。
示例:TreeSet 使用
import java.util.*;
public class TreeSetDemo {
public static void main(String[] args) {
// 1. 自然排序(实现 Comparable)
Set<Integer> numbers = new TreeSet<>();
numbers.add(5);
numbers.add(1);
numbers.add(3);
System.out.println("数字排序: " + numbers); // [1, 3, 5]
// 2. 自定义排序(Comparator)
Set<String> words = new TreeSet<>((s1, s2) -> s2.compareTo(s1)); // 逆序
words.add("Apple");
words.add("Banana");
words.add("Cherry");
System.out.println("字符串逆序: " + words); // [Cherry, Banana, Apple]
// 3. 自定义对象排序
Set<Student> students = new TreeSet<>((s1, s2) -> Integer.compare(s1.age, s2.age));
students.add(new Student("张三", 20));
students.add(new Student("李四", 18));
students.add(new Student("王五", 22));
System.out.println("学生按年龄排序: " + students);
}
// 辅助类
static class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
}
四、Map 接口及其实现
Map 存储键值对(Key-Value),键不可重复。
4.1 HashMap(基于哈希表)
✅ 特点:
- 底层结构:同
HashSet(数组 + 链表/红黑树); - 无序;
- 允许一个
null键和多个null值; - 线程不安全。
📌 底层原理(JDK 8+):
- 哈希函数:
hash(key) = (key == null) ? 0 : h ^ (h >>> 16)(扰动函数,减少冲突); - 扩容:2 倍扩容,重新计算每个键的哈希桶位置;
- 树化:链表长度 ≥ 8 且数组长度 ≥ 64 时转红黑树。
示例:HashMap 使用
import java.util.*;
public class HashMapDemo {
public static void main(String[] args) {
Map<String, Integer> scores = new HashMap<>();
// 添加键值对
scores.put("张三", 95);
scores.put("李四", 88);
scores.put("王五", 92);
scores.put(null, 100); // 允许 null 键
// 获取值
System.out.println("张三的成绩: " + scores.get("张三")); // 95
System.out.println("null 键的值: " + scores.get(null)); // 100
// 遍历(推荐 entrySet)
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// 检查键/值
System.out.println("是否包含李四: " + scores.containsKey("李四")); // true
System.out.println("是否包含成绩90: " + scores.containsValue(90)); // false
}
}
4.2 LinkedHashMap(哈希表 + 双向链表)
✅ 特点:
- 按插入顺序或访问顺序迭代;
- 性能略低于 HashMap;
- 常用于 LRU 缓存(通过
accessOrder=true)。
示例:LinkedHashMap 使用
import java.util.*;
public class LinkedHashMapDemo {
public static void main(String[] args) {
// 按插入顺序
Map<String, String> map = new LinkedHashMap<>();
map.put("1", "一");
map.put("3", "三");
map.put("2", "二");
System.out.println("插入顺序: " + map); // {1=一, 3=三, 2=二}
// 按访问顺序(LRU)
Map<String, String> lruMap = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > 3; // 最多保留3个
}
};
lruMap.put("A", "1");
lruMap.put("B", "2");
lruMap.put("C", "3");
lruMap.get("A"); // 访问 A,将其移到末尾
lruMap.put("D", "4"); // 触发移除最旧的 B
System.out.println("LRU 顺序: " + lruMap); // {C=3, A=1, D=4}
}
}
4.3 TreeMap(基于红黑树)
✅ 特点:
- 按键的自然顺序或自定义比较器排序;
- 不允许
null键(除非自定义比较器); - 提供范围操作:
subMap(),headMap(),tailMap()。
示例:TreeMap 使用
import java.util.*;
public class TreeMapDemo {
public static void main(String[] args) {
Map<Integer, String> map = new TreeMap<>();
map.put(3, "三");
map.put(1, "一");
map.put(2, "二");
System.out.println("按键排序: " + map); // {1=一, 2=二, 3=三}
// 范围查询
System.out.println("小于2的键值对: " + map.headMap(2)); // {1=一}
System.out.println("大于等于2的键值对: " + map.tailMap(2)); // {2=二, 3=三}
}
}
五、迭代器 Iterator
用于安全遍历集合,支持在遍历时删除元素(remove() 方法)。
示例:Iterator 使用
import java.util.*;
public class IteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 获取迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if ("B".equals(item)) {
it.remove(); // 安全删除(避免 ConcurrentModificationException)
}
}
System.out.println("删除 B 后: " + list); // [A, C]
// 增强 for 循环本质是 Iterator
for (String s : list) {
System.out.println(s);
}
}
}
⚠️ 注意:遍历时直接调用集合的
remove()会抛出ConcurrentModificationException!
六、工具类 Collections
提供对集合的静态操作方法。
示例:Collections 常用方法
import java.util.*;
public class CollectionsDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
// 排序
Collections.sort(list);
System.out.println("排序后: " + list); // [1, 2, 3]
// 反转
Collections.reverse(list);
System.out.println("反转后: " + list); // [3, 2, 1]
// 打乱顺序
Collections.shuffle(list);
System.out.println("打乱后: " + list);
// 查找最大/最小值
System.out.println("最大值: " + Collections.max(list)); // 3
System.out.println("最小值: " + Collections.min(list)); // 1
// 创建不可变集合(Java 9+ 推荐用 List.of())
List<String> unmodifiable = Collections.unmodifiableList(Arrays.asList("A", "B"));
// unmodifiable.add("C"); // ❌ 抛出 UnsupportedOperationException
// 同步包装(线程安全,但性能低)
List<Integer> syncList = Collections.synchronizedList(new ArrayList<>());
}
}
七、总结:集合选型指南
| 需求 | 推荐集合 |
|---|---|
| 有序、可重复、随机访问多 | ArrayList |
| 频繁首尾增删 | LinkedList |
| 去重、无序、高性能 | HashSet |
| 去重、插入顺序 | LinkedHashSet |
| 去重、排序 | TreeSet |
| 键值对、无序、高性能 | HashMap |
| 键值对、插入顺序 | LinkedHashMap |
| 键值对、按键排序 | TreeMap |
📌 核心原则:
- 默认使用
ArrayList和HashMap;- 需要排序用
TreeSet/TreeMap;- 需要插入顺序用
LinkedHashSet/LinkedHashMap;- 避免在遍历时直接修改集合,使用
Iterator.remove()。
掌握集合框架的接口设计、实现原理与适用场景,是编写高效 Java 程序的关键能力。
929

被折叠的 条评论
为什么被折叠?



