楼层: 首页/ 软件技术/ Java 基础/ 集合框架:装一堆数据的工具箱
04

集合框架:装一堆数据的工具箱

Collections · List / Set / Map · Stream API

数组能装东西,但长度定死了、不方便增删。集合框架(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);
        // 小明、小红、小刚、小丽、小强
    }
}
ConcurrentModificationException

边遍历集合边删元素,会抛并发修改异常。增强 for 循环里 list.remove(x) 是经典死法。正确姿势:要么用迭代器的 remove(),要么用 list.removeIf(x -> ...),要么用 Stream 过滤完生成新集合。别在 for-each 里手痒调用 remove。

章末面试 · 集合与 Stream(5 题)

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 转红黑树,避免哈希碰撞退化。