【问题标题】:What it mean by the API when it says the interface's method returns something?当 API 说接口的方法返回某些东西时,它是什么意思?
【发布时间】:2017-09-13 11:35:33
【问题描述】:

我知道我们无法实例化一个接口,但是在学习一本关于 Streams 的书中的教程时,我感到很困惑。

我将只使用代码部分来突出显示我不理解的部分。

// count occurences of each word in a Stream<String> sorted by word
    Map<String, Long> wordCounts =
        Files.lines(Paths.get("Chapter2Paragraph.txt"))
             .map(line -> line.replaceAll("(?!')\\p{P}", ""))
             .flatMap(line -> pattern.splitAsStream(line))
             .collect(Collectors.groupingBy(String::toLowerCase,
                TreeMap::new, Collectors.counting()));

对于方法flatMap,当在API中点击时,它会说:

返回一个流,该流包含将此流的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容的结果。每个映射流在其内容被放入该流后关闭。 (如果映射流为空,则使用空流。)

那是什么意思呢?我有点理解它的作用,但我只是不明白它在幕后是如何工作的。当API提到在这种情况下返回时它是否返回一个对象,或者它是否意味着它替换了当前流?另外,当使用Streams时,编译器是否真的创建了这些元素的对象,然后在完成后终止?

另外,从上面的代码中,我只是想确保我是正确的。 当你有一个Map&lt;String, Long&gt; wordCounts 变量时,是否意味着在流终止时,最终结果必须完全遵循类型推断?

【问题讨论】:

  • 您是在问文档是什么意思,或者它在幕后做了什么?因为这是两个几乎正交的问题。
  • 阅读这个问题,我很高兴在迁移到 Java 8 之前了解了 Python 生成器。
  • @MadPhysicist 我想知道幕后发生了什么,为了更好地理解,在尝试任何其他语言之前先学习 Python 会更好吗?
  • 您的问题是关于明确未指定的内容。该方法可能会返回一个新的Stream 实例或修改现有的实例(并返回它)。您只需要知道,您必须使用返回的实例。编译器不执行您要求的任何事情。编译器将你的源代码翻译成字节码。
  • @Scorpiorian83。不,如果您还没有基础概念的背景知识,Python 在这里帮不了您太多。

标签: java java-8 java-stream type-inference flatmap


【解决方案1】:

flapMap() 将每个元素变成一个流(任何类型)。这些流连接在一起形成一个大流。

在您的示例中,在模式上分割每一行之后(问题中未指定),整个文件都被流式传输(作为一个流)。

【讨论】:

    【解决方案2】:

    您似乎对情况的分析基本正确,但也许您对某些中间步骤感到模糊。

    您有一个方法链,这些方法在链中前一个方法的返回值上被调用。最终方法(collect)的返回值存储在名为wordCountsMap 中。不管方法做什么和返回什么(暂时),当你调用这样的方法链时,这是标准行为。

    map 的泛型类型由最终方法调用中的String::toLowerCaseCollectors.counting() 确定。前者指定键类型为String,后者指定值为Long。例如,如果您使用 String::length 作为键,那么您将获得 Map&lt;Integer, Long&gt; 类型的映射,它会计算给定长度的单词的出现次数。

    回到函数调用的顺序,可以分解如下:

    1. Files.lines(Path) 创建文件中行的Stream&lt;String&gt;。由于结果是一个流,您现在可以调用...
    2. Stream.map(Function&lt;String, String&gt;) 使用对 line.replaceAll(...) 的调用将字符串的输入流转换为另一个字符串流。
    3. 编辑后的行流现在获得Stream.flatMap(Function&lt;String, Stream&lt;String&gt;&gt;) 以将行拆分为单词并返回单个连续流。请记住,pattern.splitAsStream 将按顺序应用于每一行,因此将返回与行数一样多的流。 Stream.flatMap 获取所有这些流并将它们串成一个连续的流。

      请注意,封装的全部目的是您不必确切知道该过程在后台是如何工作的。您只需要知道最终结果是什么(在本例中为Stream&lt;String&gt;)。您应该能够交换一个实现,该实现预先将所有流读取到底层集合中,并从中返回一个流,并在处理元素时懒惰地打开每个流,而不必担心实际发生了什么。

    4. 现在文件中有Stream&lt;String&gt; 的单词,您可以应用所谓的终端操作:Stream.collect(Collector&lt;String, String, Map&lt;String, Long&gt;&gt;)。收集器由Collectors.groupingBy(Function&lt;String, String&gt;, Supplier&lt;Map&lt;String, String&gt;&gt;, Collector&lt;String, String, Long&gt;) 创建。这将创建一个收集器,该收集器根据分类器Function (String.toLowerCase()) 返回的键将输入流分组为子流,并将其传递给“下游”收集器,以对每个子流进行实际累积。结果累积存储到Supplier (TreeMap::new) 返回的映射中。下游CollectorCollectors.counting() 创建,它只计算每个流中的元素数。

    我已经扩展了此描述中的所有泛型类型,以便更容易理解并查看每个步骤产生的对象类型。

    一般来说,Java 中的流有两种类型的操作:中间操作和终端操作。流来自一个源(在这种情况下是您的文件)。所有中间操作 (1-3) 将一个流转换为另一个流。正如我在上面所展示的,输入和输出类型总是被明确定义,就像任何其他操作一样。终端操作是基于流返回某种类型的单个值的操作。在您的情况下,您计算单词频率并将它们存储到Map 中。这在java.util.stream package summary 中有很好的记录。

    【讨论】:

      猜你喜欢
      • 2023-03-27
      • 2012-01-22
      • 1970-01-01
      • 2017-07-29
      • 1970-01-01
      • 2017-08-25
      • 1970-01-01
      • 2018-04-09
      • 1970-01-01
      相关资源
      最近更新 更多