集合框架:装一堆数据的工具箱
数组能装东西,但长度定死了、不方便增删。集合框架(Collections Framework)就是 Java 提供的"动态大小容器"全家桶。核心就三张图:List(有序、可重复)、Set(不重复)、Map(键值对)。记住这三个家族,其余都是变体。
| 接口 | 特点 | 常用实现类 & 选择建议 |
|---|---|---|
| List 列表 | 有序、可重复、按下标访问。 | ArrayList:日常 90% 用它(数组实现,查询快);LinkedList:频繁头尾增删才考虑。 |
| Set 集 | 不重复、无序(或按规则排序)。 | HashSet:去重首选;TreeSet:自动排序去重。 |
| Map 映射 | 键值对 key→value,key 不重复。 | HashMap:日常首选;LinkedHashMap:按插入顺序遍历;TreeMap:按键自动排序。 |
三个最常用集合的基本操作
import java.util.*;
public class CollDemo {
public static void main(String[] args) {
// —— List:排队的队伍,可重复 ——
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
names.add("张三"); // List 允许重复,没问题
System.out.println(names.get(0)); // 张三
// —— Set:自动去重 ——
Set<String> unique = new HashSet<>(names);
System.out.println(unique.size()); // 2,第二个张三被扔掉了
// —— Map:通讯录,姓名 → 电话 ——
Map<String, Integer> scoreMap = new HashMap<>();
scoreMap.put("小明", 90);
scoreMap.put("小红", 95);
// 遍历 Map:entrySet 拿到每一对
for (Map.Entry<String, Integer> e : scoreMap.entrySet()) {
System.out.println(e.getKey() + ":" + e.getValue());
}
}
}
Stream API:集合的"流水线"(Java 8+)
以前要"从一堆学生里挑出及格的,按分数排序,取前三名",得写三四层循环。Stream 把这种操作变成一条流水线:filter(过滤)→ sorted(排序)→ limit(截取)→ collect(收集),声明"要什么",不用管"怎么循环"。
用 Stream 一行链解决"过滤 + 排序 + 收集"
List<Student> list = /* 假设有一堆学生 */;
List<Student> top3 = list.stream()
.filter(s -> s.score() >= 60) // 只要及格的
.sorted(Comparator.comparingInt(Student::score).reversed()) // 分数从高到低
.limit(3) // 只要前三名
.collect(Collectors.toList()); // 收集成新的 List
完整 Stream 链式操作案例:filter / map / sorted / collect / groupingBy / partitioningBy / reduce,含输出
import java.util.*;
import java.util.stream.*;
// 一个学生:姓名、班级、分数
record Student(String name, String clazz, int score) {}
public class StreamFullDemo {
public static void main(String[] args) {
List<Student> roster = List.of(
new Student("小明", "一班", 92),
new Student("小红", "一班", 58),
new Student("小刚", "二班", 76),
new Student("小丽", "二班", 88),
new Student("小强", "一班", 45)
);
// ① filter + map:只要及格的,提取姓名
List<String> passedNames = roster.stream()
.filter(s -> s.score() >= 60)
.map(Student::name)
.collect(Collectors.toList());
System.out.println("及格者:" + passedNames);
// 及格者:[小明, 小刚, 小丽]
// ② sorted:按分数降序
List<Student> byScore = roster.stream()
.sorted(Comparator.comparingInt(Student::score).reversed())
.collect(Collectors.toList());
System.out.println("第一名:" + byScore.get(0).name());
// 第一名:小明
// ③ groupingBy:按班级分组
Map<String, List<Student>> byClass = roster.stream()
.collect(Collectors.groupingBy(Student::clazz));
System.out.println("一班人数:" + byClass.get("一班").size());
// 一班人数:3
// ④ partitioningBy:按"是否及格"二分
Map<Boolean, List<Student>> passMap = roster.stream()
.collect(Collectors.partitioningBy(s -> s.score() >= 60));
System.out.println("不及格人数:" + passMap.get(false).size());
// 不及格人数:2
// ⑤ reduce:算总分
int total = roster.stream()
.mapToInt(Student::score)
.reduce(0, Integer::sum);
System.out.println("总分:" + total);
// 总分:359
// ⑥ joining:把所有姓名用顿号拼起来
String allNames = roster.stream()
.map(Student::name)
.collect(Collectors.joining("、"));
System.out.println(allNames);
// 小明、小红、小刚、小丽、小强
}
}
边遍历集合边删元素,会抛并发修改异常。增强 for 循环里 list.remove(x) 是经典死法。正确姿势:要么用迭代器的 remove(),要么用 list.removeIf(x -> ...),要么用 Stream 过滤完生成新集合。别在 for-each 里手痒调用 remove。
1.(选择题)日常 90% 场景下,List 选哪个实现?A. LinkedList B. ArrayList C. Vector D. Stack
查看答案
答案:B。ArrayList 是数组实现,按下标查快;LinkedList 只有频繁头尾增删才划算;Vector 是同步老古董,性能差。
2.(概念题)HashMap 和 Hashtable 区别?为什么推荐 HashMap?
查看答案
答案:Hashtable 所有方法加 synchronized,性能差;不允许 null key/value。HashMap 非线程同步,允许 null,性能好。多线程场景用 ConcurrentHashMap,别用 Hashtable。
3.(代码题)Stream 的 groupingBy 和 partitioningBy 区别?
查看答案
答案:groupingBy 按任意 key 分组(返回 Map<K, List>);partitioningBy 只能按布尔谓词二分(返回 Map<Boolean, List>),相当于分组的特例。
4.(排错题)在 for-each 循环里 list.remove(x) 会抛什么异常?正确做法?
查看答案
答案:抛 ConcurrentModificationException。正确做法:迭代器 it.remove(),或 list.removeIf(x -> ...),或 Stream 过滤生成新集合。
5.(原理题)为什么 HashMap 容量总是 2 的幂?
查看答案
答案:为了用位运算 (n - 1) & hash 代替取模 hash % n,速度快。JDK 8 后链表长度超 8 转红黑树,避免哈希碰撞退化。