跳转至

Java 8 Stream:强大的数据处理工具

简介

Java 8 引入了 Stream API,这是一个强大的功能,极大地简化了集合和数组的处理。Stream API 提供了一种声明式的方式来处理数据集合,允许你以更紧凑、更易读的代码执行复杂的数据操作,如过滤、映射、归约等。通过使用 Stream,你可以将数据处理逻辑与数据存储和遍历逻辑分离开来,提高代码的可读性和可维护性。

目录

  1. 基础概念
    • 什么是 Stream
    • Stream 与集合的区别
  2. 使用方法
    • 创建 Stream
    • 中间操作
    • 终端操作
  3. 常见实践
    • 过滤数据
    • 映射数据
    • 查找和匹配
    • 归约操作
  4. 最佳实践
    • 避免不必要的装箱和拆箱
    • 合理使用并行流
    • 结合 Stream 与 Lambda 表达式
  5. 小结

基础概念

什么是 Stream

Stream 是 Java 8 中引入的一个接口,它代表一个元素序列,可以对这些元素执行各种操作。Stream 不是数据结构,它并不存储数据,而是提供了一种对数据进行处理的方式。Stream 操作可以是顺序的,也可以是并行的。

Stream 与集合的区别

  • 数据存储:集合是数据的存储结构,它负责存储和管理数据;而 Stream 并不存储数据,它只是对现有数据的一种处理视图。
  • 遍历方式:集合通常使用迭代器或增强 for 循环进行遍历,这种遍历方式是命令式的;而 Stream 使用声明式的方式进行操作,你只需要描述要对数据执行的操作,而不需要关心具体的遍历过程。
  • 执行时机:集合的操作是立即执行的;而 Stream 的操作是延迟执行的,只有在终端操作被调用时,中间操作才会被执行,这种特性被称为惰性求值。

使用方法

创建 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 list = Arrays.asList("apple", "banana", "cherry"); Stream stream = list.stream(); stream.forEach(System.out::println); } } 2. **从数组创建**java import java.util.Arrays; import java.util.stream.Stream;

    public class StreamExample { public static void main(String[] args) { String[] array = {"apple", "banana", "cherry"}; Stream stream = Arrays.stream(array); stream.forEach(System.out::println); } } 3. **使用 `Stream.of` 方法**java import java.util.stream.Stream;

    public class StreamExample { public static void main(String[] args) { Stream stream = Stream.of("apple", "banana", "cherry"); stream.forEach(System.out::println); } } ```

中间操作

中间操作会返回一个新的 Stream,并且是惰性求值的,只有在终端操作被调用时才会执行。常见的中间操作有: 1. 过滤(filter):根据条件过滤元素。 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        Stream<Integer> filteredStream = numbers.stream()
              .filter(num -> num % 2 == 0);
        filteredStream.forEach(System.out::println);
    }
}
```
  1. 映射(map):将每个元素映射为另一个元素。 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Stream;

    public class StreamExample { public static void main(String[] args) { List words = Arrays.asList("apple", "banana", "cherry"); Stream lengthStream = words.stream() .map(String::length); lengthStream.forEach(System.out::println); } } 3. **排序(sorted)**:对 Stream 中的元素进行排序。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(3, 1, 4, 1, 5, 9); Stream sortedStream = numbers.stream() .sorted(); sortedStream.forEach(System.out::println); } } ```

终端操作

终端操作会触发 Stream 的执行,并返回一个结果。常见的终端操作有: 1. 遍历(forEach):对 Stream 中的每个元素执行指定的操作。 ```java import java.util.Arrays; import java.util.List; import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "banana", "cherry");
        list.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 squaredNumbers = numbers.stream() .map(num -> num * num) .collect(Collectors.toList()); System.out.println(squaredNumbers); } } 3. **归约(reduce)**:将 Stream 中的元素组合成一个值。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); int sum = numbers.stream() .reduce(0, Integer::sum); System.out.println(sum); } } ```

常见实践

过滤数据

过滤数据是 Stream 中最常见的操作之一。例如,从一个整数列表中过滤出所有偶数:

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

public class FilterExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        numbers.stream()
              .filter(num -> num % 2 == 0)
              .forEach(System.out::println);
    }
}

映射数据

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

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

public class MapExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry");
        words.stream()
              .map(String::length)
              .forEach(System.out::println);
    }
}

查找和匹配

查找和匹配操作可以用于检查 Stream 中是否存在满足条件的元素。例如,检查一个整数列表中是否存在大于 10 的元素:

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

public class MatchExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 11, 5);
        boolean exists = numbers.stream()
              .anyMatch(num -> num > 10);
        System.out.println(exists);
    }
}

归约操作

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

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

public class ReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int sum = numbers.stream()
              .reduce(0, Integer::sum);
        System.out.println(sum);
    }
}

最佳实践

避免不必要的装箱和拆箱

在使用 Stream 时,尽量使用原始类型的 Stream,如 IntStreamLongStreamDoubleStream,以避免不必要的装箱和拆箱操作,提高性能。

import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;

public class PrimitiveStreamExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        IntSummaryStatistics stats = IntStream.of(numbers)
              .summaryStatistics();
        System.out.println("Count: " + stats.getCount());
        System.out.println("Sum: " + stats.getSum());
        System.out.println("Average: " + stats.getAverage());
        System.out.println("Min: " + stats.getMin());
        System.out.println("Max: " + stats.getMax());
    }
}

合理使用并行流

并行流可以利用多核处理器的优势,提高数据处理的速度。但是,并行流也会带来一些开销,如线程创建和同步的开销。因此,在使用并行流时,需要根据数据量和计算复杂度来权衡利弊。

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

public class ParallelStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        int sum = numbers.parallelStream()
              .reduce(0, Integer::sum);
        System.out.println(sum);
    }
}

结合 Stream 与 Lambda 表达式

Stream API 与 Lambda 表达式是 Java 8 的两大核心特性,结合使用它们可以使代码更加简洁和易读。通过 Lambda 表达式,你可以更方便地定义 Stream 操作的逻辑。

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

public class StreamLambdaExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.stream()
              .map(num -> num * 2)
              .filter(num -> num > 5)
              .forEach(System.out::println);
    }
}

小结

Java 8 Stream API 为集合和数组的处理提供了一种强大而灵活的方式。通过使用 Stream,你可以以声明式的方式编写更紧凑、更易读的代码,提高代码的可读性和可维护性。在实际应用中,需要根据具体的需求选择合适的 Stream 操作,并遵循最佳实践,以充分发挥 Stream API 的优势。希望本文能帮助你深入理解并高效使用 Java 8 Stream。