🎯 本章你会学到:List / Map / Set 的选择、泛型与遍历时删除元素。建议边读边敲,每节练习先自己做,再展开答案对照。
Java 的集合框架是它相对 C++ 最省心的地方:不用管内存、不用管扩容,接口统一、实现可替换。日常写代码只要会挑这三种就够用。
| 需求 | 用哪个 | 特点 |
|---|---|---|
| 按顺序存放、频繁按下标读 | ArrayList | 底层数组,随机访问 O(1),中间插入 O(n) |
| 频繁在两端增删 | ArrayDeque / LinkedList | 两端操作 O(1) |
| 按键查找、统计 | HashMap | 平均 O(1),无顺序 |
| 按键有序遍历 | TreeMap | O(log n),自动排序 |
| 去重、判断存在 | HashSet | 平均 O(1),无顺序 |
| 去重且保持插入顺序 | LinkedHashSet | 去重 + 插入序 |
HashMap 做词频统计
词频统计
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Map<String, Integer> freq = new TreeMap<>(); // 有序:按 key 升序
while (sc.hasNext()) {
String w = sc.next();
freq.merge(w, 1, Integer::sum); // 不存在则置 1,否则累加
}
for (Map.Entry<String, Integer> e : freq.entrySet()) {
System.out.println(e.getKey() + " " + e.getValue());
}
}
}输入
apple banana apple运行结果
apple 2
banana 1
List 排序与去重
排序、去重与条件删除
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(List.of(3, 1, 4, 1, 5));
Collections.sort(nums);
System.out.println("sorted=" + nums);
List<Integer> dedup = new ArrayList<>(new LinkedHashSet<>(nums));
System.out.println("dedup=" + dedup);
nums.removeIf(v -> v % 2 == 0); // 条件删除的安全写法
System.out.println("odds=" + nums);
}
}运行结果
sorted=[1, 1, 3, 4, 5]
dedup=[1, 3, 4, 5]
odds=[1, 1, 3, 5]
⚠️ 易错点 遍历 List 时直接
list.remove(...) 会抛 ConcurrentModificationException。用 iterator.remove() 或 removeIf(...)。⚠️ 易错点
HashMap 的 key 必须正确实现 equals 和 hashCode。自己写的类如果只重写一个,会出现“明明 equals 相等却查不到”的诡异现象。✍️ 本节练习
6.1必做去重排序
读入 n 个整数,去重后按升序输出。
输入 第一行 n;第二行 n 个整数。
输出 一行:去重升序序列。
样例输入
6
3 1 4 1 5 3样例输出
1 3 4 5💡 提示 TreeSet<Integer> 一步到位:既去重又有序。
✅ 查看参考答案与解析
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Set<Integer> set = new TreeSet<>();
for (int i = 0; i < n; i++) {
set.add(sc.nextInt());
}
StringBuilder sb = new StringBuilder();
for (int v : set) {
if (sb.length() > 0) {
sb.append(' ');
}
sb.append(v);
}
System.out.println(sb);
}
}解析 TreeSet 底层是红黑树:插入 O(log n)、自动去重、遍历有序。数据量大时也可用 HashSet 去重后再排序。
6.2挑战同名分数累加
读入 n 行「姓名 分数」,把同名学生的分数累加起来,按姓名升序输出「姓名 总分」。
输入 第一行 n;接下来 n 行「姓名 分数」。
输出 每行「姓名 总分」,按姓名升序。
样例输入
4
Ann 90
Bob 80
Ann 10
Bob 5样例输出
Ann 100
Bob 85💡 提示 Map<String, Integer> + merge,遍历时用 TreeMap 保证有序。
✅ 查看参考答案与解析
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Map<String, Integer> total = new TreeMap<>();
for (int i = 0; i < n; i++) {
String name = sc.next();
int score = sc.nextInt();
total.merge(name, score, Integer::sum);
}
for (Map.Entry<String, Integer> e : total.entrySet()) {
System.out.println(e.getKey() + " " + e.getValue());
}
}
}解析 merge(key, value, Integer::sum) 一行完成“不存在就放入,存在就累加”,比手写 if-else 干净。
📚 本文概念都在知识大全:
JVM 与字节码 · JDK · String 与 StringBuilder · 集合框架 · 泛型 · 受检异常 · 继承与接口 · record · Stream API