Skip to content

2026-08-29 · 集合是什么、分几类、怎么遍历——集合体系的地图,先看这篇再碰具体实现。

Java 集合框架

1. 为什么需要集合

数组有一个硬伤:长度固定。存 10 个元素就得 new int[10],来了第 11 个就装不下,只能新建更大的数组再拷过去。

集合(Collection)就是来解决这个问题的容器:

对比数组集合
长度固定,不可变可变,自动扩容
存啥只能存一种类型可存任意对象类型
增删手动移位,麻烦add() / remove() 一步完成
常用方法几乎没有封装好了查找、判断、遍历

实际开发里集合的使用频率远高于数组——数据库查出来的多条记录、前端传来的列表,全部用集合装。

2. 集合的两大体系

Java 的集合分两大体系,先记住这张图(后面所有类都在这个框架里):

Collection 接口(单列集合:一个一个地存)
  ├── List 接口(有序、可重复)
  │     ├── ArrayList     ← 底层数组,查快增删慢
  │     └── LinkedList    ← 底层链表,增删快查慢

  └── Set 接口(无序、不可重复)
        ├── HashSet       ← 底层哈希表,去重
        ├── LinkedHashSet ← 哈希表+链表,去重且保序
        └── TreeSet       ← 底层二叉树,去重且排序

Map 接口(双列集合:键值对 key-value 地存)
  ├── HashMap       ← 底层哈希表(最常见)
  ├── LinkedHashMap ← 哈希表+链表,保插入顺序
  └── TreeMap       ← 底层二叉树,按键排序

一句话区分

  • Collection = 单列集合,存的是"一个个的值"(比如一串用户名)
  • Map = 双列集合,存的是"键值对"(比如 学号→学生,一个 key 对应一个 value)

Map 不继承 Collection,它是另一棵树,但统称"集合"。集合框架两大体系就是 Collection 和 Map。

3. Collection 接口的通用方法

Collection 是 List 和 Set 的父接口,所有实现类都有的方法:

java
import java.util.*;

Collection<String> c = new ArrayList<>();

c.add("张三");        // 添加元素
c.add("李四");
c.remove("张三");      // 删除元素
c.contains("李四");    // 是否包含 → true
c.size();             // 元素个数 → 1
c.isEmpty();          // 是否为空 → false
c.clear();            // 清空所有元素

Collection 类型接收 ArrayList 对象,这叫面向接口编程——只关心"这是个集合",不关心底层是数组还是链表。换实现类,代码不用改。

4. 泛型:集合的标配

不加泛型,集合里什么都能放,但取出来全是 Object,用一次强转一次:

java
// ❌ 不用泛型:取出必须强转,还可能 ClassCastException
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);   // 强转,麻烦

// ✅ 用泛型:类型写死在声明里,取出直接是 String
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);            // 不用转了

泛型的作用:编译期就拦住类型错误。List<String> 里 add 一个数字,编译直接报错,不用等运行期出问题。

泛型(Generic)从 Java 5 开始有,是集合框架的标配写法。看到 <> 尖括号,就是泛型——声明时写一次类型,全程受益。

5. 迭代器 Iterator:遍历的底层机制

5.1 为什么不能只用 for 循环

普通的 for 循环有个问题——一边遍历一边删除会出问题

java
List<String> list = new ArrayList<>(List.of("a", "b", "c"));

// ❌ 边遍历边删:会抛 ConcurrentModificationException
for (int i = 0; i < list.size(); i++) {
    if (list.get(i).equals("b")) {
        list.remove(i);  // 删元素的同时 size 变了,索引错位
    }
}

5.2 Iterator 的正确姿势

java
Iterator<String> it = list.iterator();  // 拿到迭代器
while (it.hasNext()) {                  // 还有下一个吗?
    String s = it.next();               // 取下一个
    if (s.equals("b")) {
        it.remove();                    // ✅ 用迭代器的 remove,安全
    }
}

迭代器的工作原理(想象成"游标"):

[a] → [b] → [c]

it 最初指向第一个元素前面
  hasNext() 问:后面还有吗?→ true
  next() 取走 b,游标后移一位
  remove() 删掉刚取走的那个

5.3 增强 for(foreach)就是迭代器

java
for (String s : list) {
    System.out.println(s);
}

增强 for 是迭代器的语法糖,只读遍历够用;但增强 for 里不能 remove(底层还是迭代器,删了同样报错)。要删就用 it.remove()

集合遍历三选一:只读 → 增强 for(最简洁);要删除 → 迭代器;要下标 → 普通 for。

6. 各集合的底层数据结构速查

不同集合性能不同,根源在底层数据结构不同。四种结构一句话:

数据结构一句话代表集合特点
数组连续内存、按下标直取ArrayList查快(O(1))、增删慢
链表每个节点存值和下一个的地址LinkedList增删快、查慢
哈希表按哈希值直接定位桶HashSet / HashMap查找极快、无序
二叉树左小右大的树TreeSet / TreeMap自动排序

怎么选(实际开发判断标准)

  • 存一批数据按顺序取、按下标取 → ArrayList
  • 频繁在中间插入/删除 → LinkedList
  • 要"去重" → Set 系列
  • 要"按 key 查 value"(缓存、映射)→ HashMap
  • 要排序 → TreeSet / TreeMap

记住一句话:90% 的 List 场景用 ArrayList,90% 的 Map 场景用 HashMap。 其余的是特定需求才换。

7. 本篇小结

  1. 集合 = 可变长度的容器,实际开发比数组常用
  2. 两大体系:Collection(单列:List/Set)Map(双列:键值对)
  3. Collection 通用方法:add/remove/contains/size/isEmpty/clear
  4. 泛型声明类型,编译期拦住类型错误,List<String> 标配写法
  5. 遍历:只读用增强 for;要删除用迭代器 it.remove()
  6. 底层数据结构决定性能:数组查快、链表增删快、哈希查找极快、二叉树自动排序