引言:什么是集合框架?
Java集合框架(Java Collections Framework)是一套用于存储和操作对象组的统一架构,提供了一套性能优良、使用方便的接口和实现类。
集合类比
想象一个图书馆的藏书系统:
数组:像是一个固定大小的书架
- 书必须按顺序排列
- 书架大小固定,放满了就不能再放新书
集合框架:像是图书馆的智能管理系统
- 有不同类型的书架(List/Set/Queue/Map)
- 可以根据需求自动扩展
- 查找、添加、删除都非常方便
集合框架体系结构
完整继承关系
Iterable<T>
Collection<E>
List<E>
Set<E>
Queue<E>
├─ArrayList
├─LinkedList
├─HashSet
├─TreeSet
Map<K,V>
├─HashMap
├─TreeMap
└─LinkedHashMap
四大接口对比
| 接口 | 特点 | 元素可重复? | 元素有序? | 典型实现 |
|---|---|---|---|---|
| List | 有序集合 | ✓ 可以 | ✓ 按索引 | ArrayList, LinkedList |
| Set | 不允许重复 | ✗ 不可以 | ✗ 不保证 | HashSet, TreeSet |
| Queue | 先进先出 | ✓ 可以 | ✓ 按插入顺序 | ArrayDeque, PriorityQueue |
| Map | 键值对 | 键不可重复 | 键不保证 | HashMap, TreeMap |
List接口 - 有序集合
List是一个有序、可重复的集合,可以根据索引访问元素。
List特点
- 元素按插入顺序存储
- 每个元素都有对应的索引(0, 1, 2...)
- 可以存储相同元素的多个副本
- 支持双向访问:按索引或按迭代器
ArrayList vs LinkedList
| 操作 | ArrayList | LinkedList |
|---|---|---|
| 按索引访问 | O(1) 快 | O(n) 慢 |
| 头部插入/删除 | O(n) 慢 | O(1) 快 |
| 尾部插入/删除 | 均摊O(1) 快 | O(1) 快 |
| 内存占用 | 连续空间,省内存 | 节点分散,占内存多 |
| 使用场景 | 频繁随机访问 | 频繁插入/删除 |
Set接口 - 无重复集合
Set是一个不允许重复元素的集合,适用于去重和查找。
Set实现类对比
| 实现类 | 底层结构 | 元素有序? | Null元素? | 适用场景 |
|---|---|---|---|---|
| HashSet | 哈希表 | 不保证 | 允许一个 | 通用场景,高效查找 |
| LinkedHashSet | 哈希表+链表 | 按插入顺序 | 允许一个 | 需要保持插入顺序 |
| TreeSet | 红黑树 | 按自然顺序/自定义 | 不允许 | 需要排序的场景 |
Queue接口 - 队列
Queue是一个先进先出(FIFO)的集合,适用于任务调度、缓冲等场景。
Queue常见操作
Queue queue = new LinkedList<>();
queue.offer("第一个"); // 入队
queue.offer("第二个");
queue.offer("第三个");
String first = queue.poll(); // 出队,返回"第一个"
String peek = queue.peek(); // 查看队首,不移除
Deque - 双端队列
Deque是Queue的子接口,支持两端插入和删除。
Deque deque = new ArrayDeque<>();
deque.offerFirst("头部"); // 头部插入
deque.offerLast("尾部"); // 尾部插入
String head = deque.pollFirst(); // 头部取出
String tail = deque.pollLast(); // 尾部取出
Map接口 - 键值对映射
Map存储键值对(Key-Value),键不能重复,每个键对应一个值。
Map类比:字典
Map就像一本字典:
键(Key) 值(Value)
────── ──────
"apple" → "苹果"
"banana" → "香蕉"
"orange" → "橙子"
特点:
- 键不能重复(一个英文单词只有一个解释)
- 可以通过键快速查找值
- 键值对是一一对应的
Map实现类对比
| 实现类 | 底层结构 | 键有序? | Null键? | 适用场景 |
|---|---|---|---|---|
| HashMap | 哈希表 | 不保证 | 允许一个 | 通用场景,高效查找 |
| LinkedHashMap | 哈希表+链表 | 按插入顺序 | 允许一个 | 需要保持插入顺序 |
| TreeMap | 红黑树 | 按自然顺序 | 不允许 | 需要按键排序 |
| Hashtable | 哈希表 | 不保证 | 不允许 | 需要线程安全(旧) |
Collection接口核心方法
添加操作
boolean add(E element) // 添加单个元素
boolean addAll(Collection c) // 添加整个集合
删除操作
boolean remove(Object o) // 删除单个元素
boolean removeAll(Collection c) // 删除集合中所有元素
void clear() // 清空集合
查询操作
int size() // 返回元素个数
boolean isEmpty() // 判断是否为空
boolean contains(Object o) // 判断是否包含元素
boolean containsAll(Collection c)// 判断是否包含所有元素
遍历操作
Iterator iterator() // 返回迭代器
for (E element : collection) // 增强for循环
如何选择合适的集合?
需要按索引访问? → List(ArrayList)
需要去除重复元素? → Set(HashSet)
需要按键排序? → TreeSet / TreeMap
需要键值对存储? → Map(HashMap)
需要FIFO队列? → Queue(LinkedList)
需要两端操作? → Deque(ArrayDeque)
选择决策树
需要存储键值对?
├─ 是 → Map
│ ├─ 需要排序 → TreeMap
│ └─ 不需要 → HashMap
│
└─ 否 → Collection
├─ 需要保证顺序?
│ ├─ 是,需要按索引 → List → ArrayList
│ └─ 是,需要FIFO → Queue → LinkedList/ArrayDeque
│
└─ 不需要保证顺序?
└─ Set → HashSet(通用)/ LinkedHashSet(保序)
集合使用示例
List使用示例
List languages = new ArrayList<>();
languages.add("Java");
languages.add("Python");
languages.add("Java"); // 可以重复
System.out.println(languages.get(0)); // "Java"
System.out.println(languages.size()); // 3
System.out.println(languages.indexOf("Java")); // 0
Set使用示例
Set uniqueLanguages = new HashSet<>();
uniqueLanguages.add("Java");
uniqueLanguages.add("Python");
uniqueLanguages.add("Java"); // 被忽略,不会添加
System.out.println(uniqueLanguages.size()); // 2
System.out.println(uniqueLanguages.contains("Java")); // true
Map使用示例
Map scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.put("Alice", 98); // 键相同,值覆盖
System.out.println(scores.get("Alice")); // 98
System.out.println(scores.keySet()); // [Alice, Bob]
System.out.println(scores.values()); // [98, 87]
常见错误
错误1:HashMap使用自定义对象作为键
// 错误:自定义类作为HashMap的键,必须重写hashCode和equals
class Person {
String name;
int age;
// 没有重写hashCode和equals!
}
Map map = new HashMap<>();
map.put(new Person("张三", 20), "信息");
System.out.println(map.get(new Person("张三", 20))); // null!找不到
// 正确做法:重写hashCode和equals
class Person {
String name;
int age;
@Override
public int hashCode() { return Objects.hash(name, age); }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}
错误2:HashSet/HashMap的键使用可变对象
Set> set = new HashSet<>();
List list = new ArrayList<>();
list.add("A");
set.add(list);
list.add("B"); // 修改了list的内容!
System.out.println(set.contains(list)); // false!找不到
// 原因:修改后hashCode变了,导致找不到
小结
- 集合框架提供统一的数据存储和操作架构
- List:有序、可重复、按索引访问 → ArrayList, LinkedList
- Set:无序、不可重复 → HashSet, TreeSet, LinkedHashSet
- Queue:先进先出 → LinkedList, ArrayDeque, PriorityQueue
- Map:键值对、键不重复 → HashMap, TreeMap, LinkedHashMap
- 选择集合时考虑:是否需要重复、是否需要排序、访问方式
- 自定义对象作为Map的键必须重写hashCode和equals