ZetCode

Java Stream map

最后修改于 2025 年 5 月 25 日

在本文中,我们将探讨如何有效地在 Java 流上执行 map 操作,利用函数式编程原则来高效地转换数据。

Java Stream 是一个来自数据源的元素序列,该数据源支持聚合操作。流不存储元素——相反,它们按需计算结果,这使得它们特别适用于处理大型数据集。可以从集合、数组或 I/O 资源等源使用元素,允许开发人员动态地处理数据。

流聚合操作的功能类似于 SQL 查询,使开发人员能够直接在数据流上执行过滤、映射、归约、匹配、搜索和排序。 通过利用流链接,可以按顺序应用多个操作,从而提高代码的可读性和效率。 与依赖于外部迭代的传统集合不同,流旨在使用内部迭代,从而优化执行控制和性能。

Stream Map 操作

map 方法是 Java Streams 中的一个中间操作,它允许将流元素转换为新形式,而无需修改原始源。 此方法将一个函数应用于每个元素,生成一个具有转换值的新流。

map 方法接受一个 lambda 函数作为参数,并单独处理每个元素,应用函数中定义的转换。 这使得它非常适合数据转换,例如

使用 map 方法的好处包括

map 方法是将转换应用于 Java 流中的数据的强大工具,使其成为函数式编程和现代 Java 开发的必备工具。

映射算术运算

在第一个示例中,我们映射一个值序列上的算术运算。

Main.java
void main() {

    var nums = IntStream.of(1, 2, 3, 4, 5, 6, 7, 8);
    var squares = nums.map(e -> e * e).toArray();

    System.out.println(Arrays.toString(squares));
}

在该示例中,我们创建一个整数流。 使用 map 方法,我们将算术运算应用于这些值,然后将它们转换为一个数组。

将字符串转换为用户

下一个示例演示如何将字符串流转换为自定义对象的流。

Main.java
void main() throws IOException {

    String data = """
            John,Doe
            Jane,Smith
            Alice,Johnson
            Bob,Brown
            """;

    var users = data.lines()
        .map(line -> line.split(",", 2))
        .map(fields -> new User(fields[0].trim(), fields[1].trim()))
        .toList();

    System.out.println(users);
}


record User(String firstName, String lastName) {
}

我们创建一个 User 类并将字符串映射到该类的实例。 每个字符串都被拆分为名字和姓氏,然后用于创建一个新的 User 对象。 这种方法允许我们将原始数据转换为结构化对象,以便于在我们的应用程序中进行操作和处理。

mapToInt 方法

mapToInt 方法是 map 方法的一个专门版本,它专门将流的元素映射到原始 int 值。 当处理需要转换为整数的对象流时,此方法特别有用,例如从对象中提取数字属性或对流元素执行算术运算时。

Main.java
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int sumEvenNumbers = numbers.stream()
    .mapToInt(Integer::intValue) // Convert Integer objects to primitive int
    .filter(n -> n % 2 == 0) // Keep only even numbers
    .sum(); // Compute sum

System.out.println("Sum of even numbers: " + sumEvenNumbers); 

该示例演示如何使用 mapToInt 方法将 Integer 对象流转换为原始 int 值的流。 这种转换允许进行有效的算术运算,例如过滤偶数并计算它们的总和。 当处理需要转换为原始类型的对象集合以进行数值计算时,mapToInt 方法特别有用。

映射自定义方法

在下一个示例中,我们将一个自定义方法映射到一个字符串流。

Main.java
void main() {

    var words = Stream.of("cardinal", "pen", "coin", "globe");
    words.map(this::capitalize).forEach(System.out::println);
}

String capitalize(String word) {

    word = word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
    return word;
}

该示例将流中的每个单词大写。

从 CSV 文件读取

我们可以使用 map 方法将值从 CSV 文件读取到列表中。

numbers.csv
2,3,5,6,1,0
9,5,6,3,2,1

我们在 numbers.csv 文件中有这些值。

Main.java
void main() throws IOException {

    int sum = Files.lines(Path.of("numbers.csv")) // Stream file lines directly
            .mapToInt(line -> Arrays.stream(line.split(","))
                                    .mapToInt(Integer::parseInt)
                                    .sum()) // Convert and sum each line
        .sum(); // Total sum

    System.out.println("Total sum: " + sum);
}

首先,我们将 CSV 文件读取到字符串列表中。 然后我们从该列表创建一个流,并应用映射方法来获得最终的整数列表。

var lines = Files.readAllLines(Path.of("src/resources/numbers.csv"));

使用 Files.readAllLines,我们将文件的所有行读取到字符串列表中。

int sum = Files.lines(Path.of("numbers.csv")) // Stream file lines directly
        .mapToInt(line -> Arrays.stream(line.split(","))
                                .mapToInt(Integer::parseInt)
                                .sum()) // Convert and sum each line

我们从文件行创建一个流。 每行都用逗号分割,并且每个结果字符串都使用 Integer::parseInt 转换为整数。 mapToInt 方法用于将字符串流转换为整数流。 然后应用 sum 方法来计算流中所有整数的总和。 这种方法允许有效地处理 CSV 数据,将其转换为适合进一步分析或计算的格式。

$ java Main.java
[2, 3, 5, 6, 1, 0, 9, 5, 6, 3, 2, 1]

提取特定属性

map 方法也可用于提取流中对象的特定属性。 当处理复杂对象的集合时,这特别有用,允许你专注于相关数据,而无需操作整个对象结构。

Main.java
void main() {

    var users = List.of(new User("Peter", "programmer"),
            new User("Jane", "accountant"), new User("Robert", "teacher"),
            new User("Milan", "programmer"), new User("Jane", "designer"));

    var userNames = users.stream().map(User::name).sorted().toList();
    System.out.println(userNames);

    var occupations = users.stream().map(User::occupation)
            .sorted(Comparator.reverseOrder()).distinct().toList();

    System.out.println(occupations);
}

record User(String name, String occupation) {
}

在此示例中,我们有一个 User 对象列表。 我们使用 map 方法从每个用户中提取 nameoccupation 属性。 然后将 sorted 方法应用于结果流,以按字母顺序对名称进行排序,并按相反的顺序对职业进行排序。 使用 distinct 方法来确保最终列表中仅包含唯一的职业。 最后,我们使用 collect 方法将结果收集到列表中。

$ java Main.java
[Jane, Jane, Milan, Peter, Robert]
[teacher, programmer, designer, accountant]

来源

Java Stream 文档

在本文中,我们使用了 Java Stream 映射操作。

作者

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

列出所有Java教程