Java 8 流式编程:让你的代码更优雅

2023-06-21 13:43:20 浏览数 (2227)

Java 8 引入了一个新的抽象概念,叫做流(Stream)。流可以让你以一种声明式的方式处理数据,类似于 SQL 语句。流不仅可以操作集合,还可以操作数组、文件、生成器等数据源。流还支持并行处理,可以充分利用多核 CPU 的性能。

本文将介绍 Java 8 流式编程的基本概念和常用方法,帮助你掌握流式编程的精髓。

什么是流?

流(Stream)是一个来自数据源的元素序列,并支持聚合操作。流有以下几个特点:

  • 流不存储元素,而是按需计算。
  • 流的操作不会改变源数据,而是返回一个新的流。
  • 流的操作是延迟执行的,只有当需要结果时才会执行。
  • 流支持内部迭代,无需显示地遍历元素。
  • 流支持串行和并行两种模式。

如何创建流?

创建流的方式有很多,常见的有以下几种:

  • 从集合或数组创建流。例如,List list = Arrays.asList("a", "b", "c"); Stream stream = list.stream();
  • 从 Stream 类的静态方法创建流。例如,Stream stream = Stream.of("a", "b", "c");
  • 从文件或 I/O 通道创建流。例如,Stream stream = Files.lines(Paths.get("data.txt"));
  • 从 Random 或其他生成器创建流。例如,Stream stream = Stream.generate(() -> new Random().nextInt(100));
  • 从其他类型的流创建流。例如,IntStream intStream = stream.mapToInt(String::length);

如何操作流?

流提供了很多有用的方法来对元素进行操作,这些方法可以分为两类:中间操作(Intermediate Operation)和终端操作(Terminal Operation)。

中间操作会返回一个新的流,可以链式调用多个中间操作。中间操作是惰性的,只有当遇到终端操作时才会执行。

终端操作会产生一个结果或副作用,比如计算平均值、求和、打印输出等。终端操作会消耗掉流,执行完终端操作后,流就不能再使用了。

下面介绍一些常用的中间操作和终端操作。

中间操作

  • filter:根据条件过滤元素。例如,stream.filter(s -> s.length() > 3)表示只保留长度大于3的字符串。
  • map:对每个元素进行映射,生成一个新的元素。例如,stream.map(String::toUpperCase)表示把每个字符串转换成大写。
  • flatMap:对每个元素进行映射,生成一个新的流,并把所有的流合并成一个流。例如,stream.flatMap(s -> Arrays.stream(s.split("")))表示把每个字符串拆分成字符,并合并成一个字符流。
  • distinct:去除重复的元素。例如,stream.distinct()表示去除重复的字符串。
  • sorted:对元素进行排序。例如,stream.sorted()表示按照自然顺序排序,stream.sorted(Comparator.comparing(String::length))表示按照长度排序。
  • limit:截取前n个元素。例如,stream.limit(10)表示只保留前10个元素。
  • skip:跳过前n个元素。例如,stream.skip(10)表示跳过前10个元素。

终端操作

  • forEach:对每个元素执行一个操作。例如,stream.forEach(System.out::println)表示打印每个元素。
  • count:返回元素的个数。例如,stream.count()表示返回流中元素的个数。
  • collect:将流转换成其他形式。例如,stream.collect(Collectors.toList())表示将流转换成列表,stream.collect(Collectors.joining(","))表示将流中的字符串用逗号连接起来。
  • reduce:对流中的元素进行归约操作,生成一个值。例如,stream.reduce((s1, s2) -> s1 + s2)表示将流中的字符串拼接起来,stream.reduce(0, (n1, n2) -> n1 + n2)表示将流中的整数求和。
  • min:返回最小的元素。例如,stream.min(Comparator.comparing(String::length))表示返回长度最短的字符串。
  • max:返回最大的元素。例如,stream.max(Comparator.comparing(String::length))表示返回长度最长的字符串。
  • anyMatch:判断是否有任意一个元素满足条件。例如,stream.anyMatch(s -> s.startsWith("a"))表示判断是否有以a开头的字符串。
  • allMatch:判断是否所有的元素都满足条件。例如,stream.allMatch(s -> s.length() > 3)表示判断是否所有的字符串长度都大于3。
  • noneMatch:判断是否没有任何一个元素满足条件。例如,stream.noneMatch(s -> s.contains("z"))表示判断是否没有包含z的字符串。

如何使用并行流?

并行流是指可以利用多核 CPU 并行处理的流。并行流可以提高性能,但也有一些注意事项:

  • 并行流不一定比串行流快,因为并行流需要额外的线程切换和数据同步开销。
  • 并行流不一定能保证顺序性,因为多个线程同时处理元素可能会导致乱序。
  • 并行流不适合有状态的操作,因为多个线程同时操作共享状态可能会导致数据不一致。

要创建一个并行流,有以下几种方式:

  • 调用 parallelStream() 方法。例如,list.parallelStream()表示从列表创建一个并行流。
  • 调用 parallel() 方法。例如,stream.parallel()表示把一个串行流转换成并行流。
  • 使用 StreamSupport 类。例如,StreamSupport.stream(iterable.spliterator(), true)表示从可迭代对象创建一个并行流。

要把一个并行流转换成串行流,可以调用 sequential() 方法。例如,stream.sequential()表示把一个并行流转换成串行流。

总结

本文介绍了 Java 8 流式编程的基本概念和常用方法,希望能帮助你理解和使用流式编程,让你的代码更优雅、更高效、更简洁。