ZetCode

Java Streams 中的 Identity 函数

最后修改于 2025 年 5 月 25 日

Identity 函数是函数式编程中的一个基本概念,在 Java Stream 操作中扮演着重要的角色。本教程解释了它的目的并演示了实际的用例。

Function.identity 是 Java 的 java.util.function.Function 接口中的一个静态方法,它返回一个始终返回其输入参数的函数。它本质上是一个空操作(no-op)函数,在需要函数参数但不需要转换的各种 stream 操作中非常有用。

Identity 函数在 Stream 操作(如 mapcollect)中特别有用,当你需要传递一个函数但又不想修改元素时。它提供了一种清晰、可读的方式来表达函数式管道中的“无转换”。

基本 Identity 函数用法

此示例演示了在 stream 管道中 Function.identity 的最简单用法。

Main.java
void main() {

    Stream.of("apple", "banana", "cherry")
            .map(Function.identity())
            // .map(e -> e)
            .forEach(System.out::println);
}

在这里,我们使用 Function.identity 作为映射函数,这导致 stream 元素没有任何更改。虽然此示例没有转换数据,但它显示了 identity 函数的基本语法和行为。

Function.identity 的语法等同于使用 e -> e lambda 表达式,但是使用 Function.identity 更简洁和表达力更强。

$ java Main.java
apple
banana
cherry

Collectors.toMap 中的 Identity

Function.identity 最常见的用途之一是与 Collectors.toMap 一起使用,当你希望将 stream 元素用作 map 键时。

Main.java
void main() {
    
    List<String> fruits = List.of("apple", "banana", "cherry");
    
    Map<String, Integer> fruitMap = fruits.stream()
        .collect(Collectors.toMap(
            Function.identity(),  // Use the string as key
            String::length        // Use length as value
        ));
    
    System.out.println(fruitMap);
}

在此示例中,我们创建了一个 map,其中键是水果名称(由于 Function.identity 的作用,没有更改),值是水果名称的长度。Identity 函数清楚地表明我们正在使用原始元素作为键。

$ java Main.java
{banana=6, apple=5, cherry=6}

Identity 与 Lambda 表达式

此示例将使用 Function.identity 与等效的 lambda 表达式进行比较,以演示它们的等效性。

Main.java
void main() {
    
    // Using Function.identity()
    Stream.of(1, 2, 3)
          .map(Function.identity())
          .forEach(System.out::println);
    
    // Using equivalent lambda Stream.of(1, 2, 3).map(x -> x)
    //.forEach(System.out::println); 
}

这两个版本产生相同的结果。通常首选 Function.identity,因为它更明确地表达了意图,并且在某些情况下可能具有轻微的性能优势,因为它是一个单例函数。

$ java Main.java
1
2
3
1
2
3

GroupingBy 中的 Identity

当你想要按元素本身进行分组时,Identity 函数通常与 Collectors.groupingBy 一起使用。

Main.java
void main() {
    
    List<String> words = List.of("one", "two", "three", "one", "three");
    
    Map<String, Long> counts = words.stream()
        .collect(Collectors.groupingBy(
            Function.identity(),
            Collectors.counting()
        ));
    
    System.out.println(counts);
}

在这里,我们使用 Function.identity 作为分组的分类器函数,这意味着我们按实际的字符串值进行分组。 这会生成列表中每个单词的频率计数。

$ java Main.java
{one=2, two=1, three=2}

带有 Optional 的 Identity

在 stream 操作中使用 Optional 时,Identity 函数可能很有用。

Main.java
void main() {

    List<Optional<String>> optionals = List.of(
            Optional.of("🍎 Apple"),
            Optional.empty(),
            Optional.of("🍌 Banana"),
            Optional.of("🍒 Cherry"),
            Optional.empty(),
            Optional.of("🍍 Pineapple"));

    // Extract present values without transformation
    List<String> values = optionals.stream()
            .flatMap(opt -> opt.map(Function.identity()).stream())
            .collect(Collectors.toList());

    System.out.println(values);
}

此示例演示了使用 Function.identity 来处理 Optional 值的 stream,在不转换它们的情况下提取存在的值。

$ java Main.java
[🍎 Apple, 🍌 Banana, 🍒 Cherry, 🍍 Pineapple]

来源

Java Function.identity() 文档

Identity 函数是 Java Streams 中一个简单但功能强大的工具,它清楚地表达了函数式管道中“无转换”的意图。 虽然它不修改数据,但它在需要函数参数的 stream 操作中起着重要的作用。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 我从 2007 年开始撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有Java教程