系列中的上一篇
当前教程
将数据源转换为流
这是本系列的最后一篇!

系列中的上一篇: 使用转换转换迭代

将数据源转换为流

此页面由 Venkat SubramaniamUPL 下贡献

 

以流的方式思考

在本 教程系列 的前几篇文章中,我们研究了如何将以命令式风格编写的循环转换为函数式风格。在本文中,我们将研究如何从函数式角度看待数据源,将其视为数据流,并将迭代转换为使用 Streams API。

我们已经了解了如何使用 filter()map() 函数分别选择和转换数据。我们可以在函数式管道中间执行这些操作。在前面文章中的示例中,我们使用了 range()rangeClosed() 等函数来创建一定范围内的数字流。当我们想要遍历已知范围内的值时,这非常有效,但是,我们经常需要处理来自外部资源的数据,例如来自文件。如果我们能够将外部资源视为流,那么就可以轻松地应用函数式管道的操作。在本文中,我们将通过一个示例来说明这个想法。

 

从命令式风格到函数式风格

假设我们想要遍历一个文件,并统计包含一个或多个特定单词的行数。以下是一个非常常见的命令式风格代码,用于完成此任务

//Sample.java
import java.nio.file.*;

public class Sample {
  public static void main(String[] args) {
    try {
      final var filePath = "./Sample.java";
      final var wordOfInterest = "public";

      try (var reader = Files.newBufferedReader(Path.of(filePath))) {
        String line = "";
        long count = 0;

        while((line = reader.readLine()) != null) {
          if(line.contains(wordOfInterest)) {
            count++;
          }
        }

        System.out.println(String.format("Found %d lines with the word %s", count, wordOfInterest));
      }
    } catch(Exception ex) {
      System.out.println("ERROR: " + ex.getMessage());
    }
  }
}

为了便于处理此示例,我们将在与代码相同的源文件中查找包含单词“public”的行数。您可以根据需要更改 filePath 的值以指向不同的文件,或更改 wordOfInterest 的值以指向其他单词。

此示例主要包含两个部分。我们使用 newBufferedReader() 方法返回的 BufferedReader 来访问我们要查看的文件的内容。然后,在 while 循环中,我们检查每一行是否包含所需的单词,如果包含,则将 count 加 1,以指示我们找到了另一行包含该单词的行。让我们分别检查这两个部分,先从第二个部分开始。

仔细观察循环,从我们之前文章中的讨论中,我们可以认识到 if 的存在表明,如果我们可以将代码编写为函数式管道,那么我们可以使用 filter() 操作。一旦我们过滤掉或选择包含所需单词的行,就可以使用流的 count() 方法来统计行数。您可能非常好奇,迫不及待地想问:“但是,流在哪里?”为了回答这个问题,让我们看一下代码的第一部分。

数据,即文本行,来自路径存储在变量 filePath 中的文件。我们使用 BufferedReaderreadLine() 方法和命令式风格来遍历每一行文本。为了使用函数式管道,以及 filter() 等操作,我们需要一个数据 Stream。因此问题是,“是否可以为文件中的内容获取数据流?”

答案是肯定的。JDK 和 Java 语言的开发人员不仅引入了进行函数式编程的功能,还说“祝你好运”。他们煞费苦心地增强了 JDK,添加了函数,以便我们作为程序员能够充分利用 Java 的函数式功能来完成日常任务。

将文件内容转换为数据流的一种简单方法是使用 java.nio.file 包中 Files 类提供的 lines() 方法。让我们使用 lines() 方法(它为我们提供了文件内容的 Stream)将之前的命令式风格代码重构为函数式风格,如下所示

//Sample.java
import java.nio.file.*;

public class Sample {
  public static void main(String[] args) {
    try {
      final var filePath = "./Sample.java";
      final var wordOfInterest = "public";

      try(var stream = Files.lines(Path.of(filePath))) {
        long count = stream.filter(line -> line.contains(wordOfInterest))
          .count();

        System.out.println(String.format("Found %d lines with the word %s", count, wordOfInterest));
      }
    } catch(Exception ex) {
      System.out.println("ERROR: " + ex.getMessage());
    }
  }
}

lines() 方法不仅提供了文件内容的数据流,还消除了读取行周围的大量冗余代码。与我们一次获取一行文本的外部迭代器不同,流使我们能够使用内部迭代器,从而可以专注于对流管道中出现的每一行文本执行的操作。

 

映射

无论何时处理来自外部资源的数据集合,都要询问是否有方法获取该资源内容的数据流。您很有可能在 JDK 或第三方库中找到相应的函数。一旦我们获得了流,就可以使用 filter()map() 等高效的函数式运算符来流畅地遍历资源中包含的数据集合。


上次更新: 2024 年 4 月 23 日


系列中的上一篇
当前教程
将数据源转换为流
这是本系列的最后一篇!

系列中的上一篇: 使用转换转换迭代