跳转至

Java Stream Example 深度解析

简介

在 Java 8 引入 Stream API 后,处理集合数据的方式发生了重大变革。Stream API 提供了一种简洁且强大的方式来对集合进行过滤、映射、归约等操作,使代码更具可读性和表现力。本文将通过丰富的示例深入探讨 Java Stream 的基础概念、使用方法、常见实践以及最佳实践,帮助读者全面掌握这一重要特性。

目录

  1. 基础概念
  2. 使用方法
    • 创建 Stream
    • 中间操作
    • 终端操作
  3. 常见实践
    • 过滤数据
    • 映射数据
    • 归约数据
  4. 最佳实践
    • 性能优化
    • 避免副作用
    • 合理选择操作
  5. 小结
  6. 参考资料

基础概念

Stream 是 Java 8 中引入的一个接口,它代表了一系列支持顺序和并行聚合操作的元素。Stream 本身并不存储数据,而是基于数据源(如集合、数组等)创建,并在其上执行各种操作。

与传统的集合遍历方式不同,Stream API 使用一种声明式的方式处理数据,即描述要做什么,而不是具体如何做。这使得代码更加简洁和易读,同时也能充分利用多核处理器进行并行处理。

使用方法

创建 Stream

  1. 从集合创建 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Stream;

    public class StreamExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 3, 4, 5); Stream streamFromList = numbers.stream(); Stream parallelStreamFromList = numbers.parallelStream(); } } 2. **从数组创建**java import java.util.stream.Stream;

    public class StreamExample { public static void main(String[] args) { int[] array = {1, 2, 3, 4, 5}; IntStream streamFromArray = Arrays.stream(array); } } 3. **使用 `Stream.of()` 方法**java import java.util.stream.Stream;

    public class StreamExample { public static void main(String[] args) { Stream streamFromString = Stream.of("apple", "banana", "cherry"); } } ```

中间操作

中间操作会返回一个新的 Stream,并且可以链式调用。常见的中间操作包括 filtermapdistinct 等。 1. filter 操作:用于过滤 Stream 中的元素。 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Collectors;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> evenNumbers = numbers.stream()
             .filter(n -> n % 2 == 0)
             .collect(Collectors.toList());
        System.out.println(evenNumbers); // 输出: [2, 4]
    }
}
```
  1. map 操作:用于将 Stream 中的每个元素映射为另一个元素。 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Collectors;

    public class StreamExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 3, 4, 5); List squaredNumbers = numbers.stream() .map(n -> n * n) .collect(Collectors.toList()); System.out.println(squaredNumbers); // 输出: [1, 4, 9, 16, 25] } } 3. **`distinct` 操作**:用于去除 Stream 中的重复元素。java import java.util.Arrays; import java.util.List; import java.util.stream.Collectors;

    public class StreamExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5); List distinctNumbers = numbers.stream() .distinct() .collect(Collectors.toList()); System.out.println(distinctNumbers); // 输出: [1, 2, 3, 4, 5] } } ```

终端操作

终端操作会触发 Stream 的处理,并返回一个结果。常见的终端操作包括 forEachcollectreduce 等。 1. forEach 操作:用于遍历 Stream 中的每个元素并执行指定的操作。 ```java import java.util.Arrays; import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.stream()
             .forEach(System.out::println);
    }
}
```
  1. collect 操作:用于将 Stream 中的元素收集到一个集合中。 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Collectors;

    public class StreamExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 3, 4, 5); List result = numbers.stream() .filter(n -> n > 2) .collect(Collectors.toList()); System.out.println(result); // 输出: [3, 4, 5] } } 3. **`reduce` 操作**:用于将 Stream 中的元素归约为一个值。java import java.util.Arrays; import java.util.List;

    public class StreamExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream() .reduce(0, (a, b) -> a + b); System.out.println(sum); // 输出: 15 } } ```

常见实践

过滤数据

在处理集合时,经常需要根据某些条件过滤出符合要求的元素。例如,从一个学生列表中筛选出成绩大于 80 分的学生。

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public int getScore() {
        return score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

public class StreamFilterExample {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 85));
        students.add(new Student("Bob", 70));
        students.add(new Student("Charlie", 90));

        List<Student> highScorers = students.stream()
             .filter(student -> student.getScore() > 80)
             .collect(Collectors.toList());

        System.out.println(highScorers);
    }
}

映射数据

映射操作可以将一种类型的元素转换为另一种类型。例如,将一个字符串列表中的每个字符串转换为其长度。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamMapExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry");
        List<Integer> wordLengths = words.stream()
             .map(String::length)
             .collect(Collectors.toList());
        System.out.println(wordLengths); // 输出: [5, 6, 6]
    }
}

归约数据

归约操作可以将 Stream 中的所有元素组合成一个值。例如,计算一个整数列表的乘积。

import java.util.Arrays;
import java.util.List;

public class StreamReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int product = numbers.stream()
             .reduce(1, (a, b) -> a * b);
        System.out.println(product); // 输出: 120
    }
}

最佳实践

性能优化

  1. 使用并行 Stream:对于大规模数据集,使用并行 Stream 可以充分利用多核处理器的优势,提高处理速度。但需要注意的是,并行 Stream 并不总是比顺序 Stream 快,在某些情况下,顺序 Stream 可能更高效。 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Collectors;

    public class ParallelStreamExample { public static void main(String[] args) { List numbers = Arrays.asList(1, 2, 3, 4, 5); List result = numbers.parallelStream() .map(n -> n * n) .collect(Collectors.toList()); System.out.println(result); } } ``` 2. 避免不必要的中间操作:过多的中间操作可能会影响性能,尽量简化 Stream 的操作链。

避免副作用

Stream API 设计的初衷是鼓励无副作用的操作。在 forEachmap 等操作中,应避免修改外部状态,因为并行 Stream 可能会导致不可预测的结果。

合理选择操作

根据具体需求选择合适的 Stream 操作。例如,如果只需要判断 Stream 中是否存在某个元素,使用 anyMatchfilter 后再检查集合大小更高效。

小结

Java Stream API 为处理集合数据提供了一种强大而简洁的方式。通过掌握基础概念、使用方法、常见实践以及最佳实践,开发者能够编写出更高效、更易读的代码。Stream API 的声明式风格不仅提高了开发效率,还能充分利用多核处理器进行并行处理,是 Java 开发者不可或缺的工具。

参考资料

  1. Oracle Java Documentation - Stream API
  2. Effective Java, Third Edition - Joshua Bloch
  3. Java 8 in Action - Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft