← Java 学习路线(共 8 章)

第 6 章 集合框架

red wenzi · 2026-09-15 · 编程语言 · Java · 📖 预计阅读 12 分钟 · 共 2 道练习
🎯 本章你会学到:List / Map / Set 的选择、泛型与遍历时删除元素。建议边读边敲,每节练习先自己做,再展开答案对照。

Java 的集合框架是它相对 C++ 最省心的地方:不用管内存、不用管扩容,接口统一、实现可替换。日常写代码只要会挑这三种就够用。

需求用哪个特点
按顺序存放、频繁按下标读ArrayList底层数组,随机访问 O(1),中间插入 O(n)
频繁在两端增删ArrayDeque / LinkedList两端操作 O(1)
按键查找、统计HashMap平均 O(1),无顺序
按键有序遍历TreeMapO(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 必须正确实现 equalshashCode。自己写的类如果只重写一个,会出现“明明 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