1. 什么是 Collection 接口
在 Java 中,接口就像一个契约:如果一个类实现了接口,它必须提供接口中所有方法的实现。接口本身不保存状态,也不包含代码(几乎如此:自 Java 8 起支持default 方法,但这里更重要的是基本理念)。
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();
// ... 以及一大堆有用的方法
}
关键方法:
- add(E e) — 添加元素。
- remove(Object o) — 删除元素。
- size() — 获取元素数量。
- isEmpty() — 检查集合是否为空。
- contains(Object o) — 检查元素是否存在。
- clear() — 清空集合。
- iterator() — 获取用于遍历的迭代器。
为什么 Map 不继承 Collection?
Map 在集合中是个特例。因为 Map 是由“键 → 值”对组成的集合,而不是简单的元素集合。它有不同的契约和方法集。比如,Map 中没有 add 方法,因为添加通过 put(key, value) 完成;而且不知道键就去检查值是否存在也没有意义。
2. 集合的层次结构
主要接口的“树形图”如下:
Iterable
|
Collection
/ | \
List Set Queue
|
Deque
- Iterable — 最基础的接口:凡是可以在 for-each 循环中遍历的东西。
- Collection — 扩展了 Iterable,增加了对元素集合的操作。
- 三大分支:
- List — 有序列表,允许重复。
- Set — 唯一元素的集合,顺序取决于实现。
- Queue — 队列(通常是 FIFO);其子类是 Deque(双端队列)。
而 Map 则是单独的一支:
Map
/ \
HashMap TreeMap
层次结构可视化(示意图)
+--------------------+
| Iterable<E> |
+--------------------+
|
+--------------------+
| Collection<E> |
+--------------------+
/ | \
+--------+ +-------+ +-------+
| List | | Set | | Queue |
+--------+ +-------+ +-------+
|
+------+
|Deque |
+------+
+--------------------+
| Map<K,V> |
+--------------------+
3. List:有序且可包含重复元素的集合
List 是一种集合,它:
- 重视元素的顺序(第一个、第二个、第三个……)。
- 允许存在相同的元素(重复项)。
- 可以按索引获取元素(如 list.get(2))、替换元素、在任意位置插入。
实现示例: ArrayList —— 按索引访问速度快;LinkedList —— 适合在中间频繁插入/删除。
import java.util.*;
List<String> shoppingList = new ArrayList<>();
shoppingList.add("Milk");
shoppingList.add("Bread");
shoppingList.add("Cheese");
shoppingList.add("Bread"); // 允许重复元素!
System.out.println(shoppingList.get(1)); // "Bread"
List 常用方法:
- add(E e)、add(int index, E e)
- get(int index)、set(int index, E e)
- remove(int index)、remove(Object o)
- indexOf(Object o)、lastIndexOf(Object o)
4. Set:唯一元素的集合
Set 是一种集合,它:
- 每个元素都是唯一的(不允许重复)。
- HashSet 不保证顺序,TreeSet 可以排序,LinkedHashSet 保持插入顺序。
实现示例: HashSet、TreeSet、LinkedHashSet。
import java.util.*;
Set<String> uniqueNames = new HashSet<>();
uniqueNames.add("安娅");
uniqueNames.add("鲍里斯");
uniqueNames.add("安娅"); // 不会添加——已经存在!
System.out.println(uniqueNames.contains("安娅")); // true
System.out.println(uniqueNames.size()); // 2
Set 常用方法:
- add(E e)、remove(Object o)
- contains(Object o)
- size()、isEmpty()
5. Map:键-值对集合
Map 是一种集合,它:
- 每个元素都是一对键 → 值。
- 键唯一,值可以重复。
- 可通过键进行快速访问。
实现示例: HashMap、TreeMap、LinkedHashMap。
import java.util.*;
Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("安娅", "+19991112233");
phoneBook.put("鲍里斯", "+19994445566");
phoneBook.put("安娅", "+19990001122"); // 将覆盖安娅的号码!
System.out.println(phoneBook.get("安娅")); // "+19990001122"
System.out.println(phoneBook.containsKey("鲍里斯")); // true
Map 常用方法:
- put(K key, V value)、get(K key)
- remove(K key)
- containsKey(K key)、containsValue(V value)
- keySet()、values()、entrySet()
6. 层次结构可视化:一图总结
简要汇总表:
| 接口 | 说明 | 实现示例 | 关键特性 |
|---|---|---|---|
|
有序列表 | |
按索引访问,允许重复 |
|
唯一值集合 | |
仅唯一元素 |
|
键-值对 | |
键唯一,值不限 |
层次结构示意:
Collection
/ | \
List Set Queue
|
Deque
Map(独立)
7. 何时使用哪种接口
List
- 当元素顺序很重要时(例如用户操作历史)。
- 当需要重复项时(例如订单中的多件相同商品)。
- 当需要按索引快速访问/替换时。
Set
- 当需要唯一性时(email、登录名、id)。
- 当不关心顺序,或恰好需要自动排序的集合(TreeSet)时。
Map
- 当需要将键与值进行映射时(id → 对象,登录名 → 个人资料)。
- 当需要按键快速查找时。
- 当键必须唯一而值可以不唯一时。
8. 使用集合层次结构时的常见错误
错误 No. 1:在变量声明中使用错误的接口。 如果你写的是 ArrayList<String> list = new ArrayList<>();,就把自己和具体实现绑死了。更推荐 List<String> list = new ArrayList<>(); —— 这样更容易替换实现。
错误 No. 2:向 Set 添加重复项并期待它们出现。 这不是 bug,而是特性:按照定义,Set 不存储重复项。再次插入会被忽略。
错误 No. 3:把 Map 当作普通集合来使用。 Map 不是列表也不是集合。遍历时请使用 keySet()、values() 或 entrySet()。
错误 No. 4:指望 HashSet 或 HashMap 有顺序。 HashSet 和 HashMap 不保证顺序。如果顺序很重要——请使用 LinkedHashSet 或 LinkedHashMap。
错误 No. 5:用 List 存储唯一元素。 如果需要唯一性——请使用 Set。List 不会阻止重复项。
GO TO FULL VERSION