0Pricing
Java Academy · 课时

toMap、joining 与 summarizing

将流转换为映射,使用 joining 拼接字符串,并通过 summarizingInt 计算统计数据

toMap、joining 与 summarizing 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

Collectors.toMap 基础

Collectors.toMap(keyMapper, valueMapper) 会将流转换为一个映射。每个元素提供一个键值对。

import java.util.*;
import java.util.stream.*;

record Country(String code, String name) {}
List<Country> countries = List.of(
    new Country("US","United States"),
    new Country("DE","Germany"),
    new Country("JP","Japan")
);

Map<String, String> codeToName =
    countries.stream().collect(
        Collectors.toMap(Country::code, Country::name)
    );
System.out.println(codeToName.get("DE")); // Germany

处理重复键

toMap 遇到重复键时会抛出 IllegalStateException。将合并函数作为第三个参数传入,以解决冲突:

List<String> words = List.of("apple","ant","banana","bat","cherry");

Map<Character, String> firstByLetter =
    words.stream().collect(
        Collectors.toMap(
            w -> w.charAt(0),
            w -> w,
            (existing, replacement) -> existing // keep first
        )
    );
System.out.println(firstByLetter); // {a=apple, b=banana, c=cherry}

使用 toMap 转换值

值映射器可以是任意函数——先转换元素,再将其存储:

record User(String login, String email) {}
List<User> users = List.of(
    new User("alice","alice@example.com"),
    new User("bob","bob@example.com")
);

Map<String, String> loginToEmail =
    users.stream().collect(
        Collectors.toMap(User::login, u -> u.email().toUpperCase())
    );
System.out.println(loginToEmail.get("alice")); // ALICE@EXAMPLE.COM

toUnmodifiableMap

使用 Collectors.toUnmodifiableMap() 生成不可变映射——它等价于使用 Collections.unmodifiableMap() 包装,但写法更简洁:

Map<String, Integer> wordLengths =
    Stream.of("Java","Stream","API")
          .collect(Collectors.toUnmodifiableMap(w -> w, String::length));
// wordLengths.put("test",4); // throws UnsupportedOperationException

Collectors.joining

Collectors.joining() 会连接字符串元素。它接受可选的分隔符、前缀和后缀:

List<String> names = List.of("Alice","Bob","Carol");

System.out.println(names.stream().collect(Collectors.joining()));
// AliceBobCarol

System.out.println(names.stream().collect(Collectors.joining(", ")));
// Alice, Bob, Carol

System.out.println(names.stream().collect(Collectors.joining(", ", "[", "]")));
// [Alice, Bob, Carol]

使用 joining 输出 CSV

使用 joining 构建 CSV 行或 SQL IN 子句:

List<Integer> ids = List.of(1,2,3,4,5);

String inClause = ids.stream()
    .map(Object::toString)
    .collect(Collectors.joining(",", "(", ")"));
System.out.println("WHERE id IN " + inClause);
// WHERE id IN (1,2,3,4,5)

summarizingInt / Long / Double

Collectors.summarizingInt() 一次遍历即可计算所有统计数据,并返回一个 IntSummaryStatistics:

record Product(String name, int price) {}
List<Product> products = List.of(
    new Product("A",100), new Product("B",200), new Product("C",150)
);

IntSummaryStatistics stats =
    products.stream().collect(Collectors.summarizingInt(Product::price));

System.out.println("Count: " + stats.getCount());   // 3
System.out.println("Sum: "   + stats.getSum());     // 450
System.out.println("Min: "   + stats.getMin());     // 100
System.out.println("Max: "   + stats.getMax());     // 200
System.out.println("Avg: "   + stats.getAverage()); // 150.0

summingInt 和 averagingInt

在只需要一种统计数据的简单场景中:

int total = products.stream().collect(Collectors.summingInt(Product::price));
System.out.println("Total: " + total); // 450

double avg = products.stream().collect(Collectors.averagingInt(Product::price));
System.out.println("Average: " + avg); // 150.0

使用 summarizingInt 进行 groupingBy

按分组组合统计操作:

record Sale(String region, int amount) {}
List<Sale> sales = List.of(
    new Sale("North",100), new Sale("South",200),
    new Sale("North",150), new Sale("South",300)
);

Map<String, IntSummaryStatistics> statsByRegion =
    sales.stream().collect(
        Collectors.groupingBy(Sale::region,
            Collectors.summarizingInt(Sale::amount))
    );
statsByRegion.forEach((r,s) ->
    System.out.printf("%s: sum=%d avg=%.1f%n", r, s.getSum(), s.getAverage()));

收集到 LinkedHashMap

默认情况下,toMap 会生成 HashMap(无序)。使用四参数重载指定 LinkedHashMap,以保留插入顺序:

Map<String, Integer> ordered =
    Stream.of("banana","apple","cherry")
          .collect(Collectors.toMap(
              w -> w,
              String::length,
              (a,b) -> a,
              LinkedHashMap::new
          ));
System.out.println(ordered.keySet()); // [banana, apple, cherry]

实际应用:配置解析器

使用 toMap 将 key=value 行解析为映射:

List<String> lines = List.of("host=localhost","port=5432","db=myapp");

Map<String, String> config =
    lines.stream()
         .map(l -> l.split("=", 2))
         .collect(Collectors.toMap(a -> a[0], a -> a[1]));

System.out.println(config.get("port")); // 5432

快速检查

您在包含两个相同键元素的流上使用 Collectors.toMap(k, v)。会发生什么?

回顾:toMap、joining、summarizing

要点回顾:

  • toMap(keyMapper, valueMapper)——没有合并函数时,遇到重复键会抛出异常
  • joining(delimiter, prefix, suffix)——简洁地连接字符串
  • summarizingInt 一次遍历即可得到数量、总和、最小值、最大值和平均值
  • 对于单项统计需求,使用 summingInt/averagingInt
  • 使用带有 LinkedHashMap::new 的四参数 toMap,获得有序结果

常见问题解答

「toMap、joining 与 summarizing」课时是免费的吗?

是的 — 「toMap、joining 与 summarizing」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「toMap、joining 与 summarizing」这节课中我会学到什么?

将流转换为映射,使用 joining 拼接字符串,并通过 summarizingInt 计算统计数据 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「toMap、joining 与 summarizing」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. groupingBy:元素分类
  2. partitioningBy 与计数
  3. toMap、joining 与 summarizing
  4. 构建自定义收集器
← 返回 Java Academy