Appearance
2026-08-29 · List:有序可重复;Set:去重。日常用 ArrayList + HashSet 就覆盖 90% 场景。
Java List 与 Set 集合
1. List 接口:有序、可重复
List 的两个特点:
- 有序:按添加顺序排列(下标从 0 开始)
- 可重复:同一个元素可以存多份
java
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("张三"); // ✅ 允许重复
System.out.println(list); // [张三, 李四, 张三]
System.out.println(list.get(1)); // 李四(按下标取)List 特有方法(全靠下标):
| 方法 | 作用 |
|---|---|
get(index) | 按下标取元素 |
set(index, 元素) | 修改指定位置的元素 |
indexOf(元素) | 查找元素第一次出现的位置,找不到返回 -1 |
remove(index) | 按下标删除 |
subList(0, 2) | 截取子列表 |
2. ArrayList:底层是数组(最常用)
java
List<String> list = new ArrayList<>(); // 默认容量 10
List<String> list2 = new ArrayList<>(100); // 提前指定容量(要存很多就指定)
list.add("a");
list.add("b");
list.remove(0); // 按下标删
list.remove("b"); // 按元素删(第一个匹配的)
list.get(0); // 按下标查
list.contains("a"); // 是否包含
list.size(); // 长度(不是 length,是方法)为什么"查快、增删慢":
- 底层是数组(连续内存),按下标查直接算地址,O(1) 极快
- 中间插入/删除,后面所有元素都要往后/往前挪一位,O(n) 慢
扩容机制:默认容量 10,装满了扩容为原来的 1.5 倍,把旧数据拷进新数组。扩容要整体拷贝,所以预知要存大量数据时直接 new ArrayList<>(容量)。
3. LinkedList:底层是链表(了解即可)
每个节点存自己的值 + 下一个节点的地址,链式相连:
[a] → [b] → [c] → null- 增删快:中间插入只改两个节点的"箭头",O(1)
- 查得慢:没有下标,要找第 5 个必须从头走过去,O(n)
实际开发 90% 用 ArrayList。LinkedList 的"增删快"只在中间频繁插入删除的场景才成立,绝大多数业务是"末尾追加 + 按条件查",ArrayList 更合适。
4. Set 接口:去重
Set 的特点:不重复——加重复元素会被拒绝;无下标——不能 get(i)。
java
Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
set.add("a"); // ❌ 重复,加不进去
System.out.println(set); // [a, b](只有一个 a)
// Set 没有 get(index),遍历用增强 for
for (String s : set) {
System.out.println(s);
}Set 靠 equals 判断重复——去重就是"先算 hashCode 定位,再看 equals 是否相等,相等就拒绝"。
5. HashSet(去重主力)
java
Set<String> set = new HashSet<>(); // 底层哈希表,查重 O(1),极快
set.add("a");
set.add("b");特点:
- 无序(遍历顺序不稳定,跟哈希值有关)
- 查重极快(不用遍历全部,直接算哈希定位)
⚠️ 坑:自定义对象必须重写 equals + hashCode
对 String、Integer 这类 JDK 自带类,去重自动生效。但自定义类不重写这两个方法,去重失效:
java
class Student {
String name;
int age;
// 没重写 equals/hashCode → 每个 new 出来的都是"不同的对象"
}
Set<Student> set = new HashSet<>();
set.add(new Student("张三", 20));
set.add(new Student("张三", 20)); // ❌ 加进去了!两个都进IDEA 快捷键 Alt+Insert → equals() and hashCode(),自动生成,别手写。
规矩:重写 equals 必须同时重写 hashCode。
6. LinkedHashSet:去重 + 保插入顺序
java
Set<String> set = new LinkedHashSet<>();
set.add("c");
set.add("a");
set.add("b");
System.out.println(set); // [c, a, b](按添加顺序)场景:去重一批用户,且要保持原来的顺序 → LinkedHashSet。
7. TreeSet:去重 + 自动排序(了解即可)
元素放进去自动升序排列:
java
Set<Integer> set = new TreeSet<>();
set.add(5);
set.add(1);
set.add(3);
System.out.println(set); // [1, 3, 5]底层是红黑树(自平衡二叉树),元素自动排序但性能最慢。自定义对象排序要实现 Comparable 接口,或用构造传 Comparator(Lambda):
java
Set<Student> set = new TreeSet<>((s1, s2) -> s1.age - s2.age); // 按年龄排序业务里"要排序的集合"大部分直接用 SQL 的
ORDER BY或 Stream 的sorted()解决,TreeSet 用得少,知道有这功能即可。
8. 选型小结
| 场景 | 选哪个 |
|---|---|
| 存一批数据按顺序取 | ArrayList(默认) |
| 去重 | HashSet |
| 去重 + 保顺序 | LinkedHashSet |
| 去重 + 排序 | TreeSet(少用,排序交给 SQL/Stream) |