2017-04-17 76 views
1

我知道我们不能实例化一个接口,但是在做关于Streams的书的教程时,我感到困惑。当它说接口的方法返回什么时,API是什么意思?

我只是用部分代码来突出显示我不明白的部分。

// 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时,编译器是否实际创建了这些元素的Object,然后在完成时终止?

此外,从上面的代码,我只是想确保我是正确的。 当你有一个Map<String, Long> wordCounts变量时,这是否意味着在流终止时,最终结果必须严格遵循类型推断?

+1

你在问这些文档是什么意思,或者它在幕后做了什么?因为这是两个近乎正交的问题。 –

+1

阅读这个问题,我很高兴在移动到Java版本8之前学习了Python生成器。 –

+0

@MadPhysicist我想知道幕后发生了什么,在尝试任何操作之前先学习Python更好其他语言为了更好地理解? – Scorpiorian83

回答

1

您似乎已经大致正确地分析了这种情况,但也许您对某些中间步骤感到朦胧。

您有一系列方法在链中前一个方法的返回值上调用。最终方法(collect)的返回值存储在名为wordCountsMap中。无视这些方法到底做了些什么以及它们返回的内容(只是暂时的),当你调用这样的方法链时,这是标准行为。

地图的一般类型由最终方法调用中的String::toLowerCaseCollectors.counting()决定。前者指定密钥类型为String,后者指定值为Long。例如,如果您使用String::length作为关键字,那么您应该得到一个类型为Map<Integer, Long>的地图,它将计算具有给定长度的单词的出现次数。

让我们回到函数调用的顺序,可以细分如下:

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

    请注意,封装的全部目的是您不必知道引擎盖下的过程是如何工作的。你只需要知道最终结果是什么(在这种情况下是Stream<String>)。您应该能够将预先读取所有流的实现交换为基础集合,并从中返回一个流,其中一个会在处理元素时懒散地打开每个流,而不必担心实际发生的情况。

  4. 现在您已在文件中有一个Stream<String>字词,您可以应用所谓的终端操作:Stream.collect(Collector<String, String, Map<String, Long>>)。收藏家由Collectors.groupingBy(Function<String, String>, Supplier<Map<String, String>>, Collector<String, String, Long>)创建。这创建了一个收集器,该收集器根据分类器FunctionString.toLowerCase())返回的密钥将输入流分组为子流,并将其传递到“下游”收集器,以在每个子流上进行实际的累加。产生的积累存储在由SupplierTreeMap::new)返回的映射中。下游CollectorCollectors.counting()创建,它只计算每个流中元素的数量。

我已经扩展了本说明中的所有泛型,使其更容易遵循,并查看每步产生的对象种类。

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

2

flapMap()将每个元素转换为任何类型的流。溪流串联在一起形成一条大溪流。

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

相关问题