【问题标题】:Java 8: Applying Stream map and filter in one goJava 8:一次性应用流图和过滤器
【发布时间】:2015-01-02 23:19:15
【问题描述】:

我正在为 Java 8 中的文件编写解析器。使用 Files.lines 读取文件并返回顺序的 Stream<String>

每一行都映射到一个数据对象Result,如下所示:

Result parse(String _line) {
  // ... code here
  Result _result = new Result().
  if (/* line is not needed */) {
    return null;
  } else {
    /* parse line into result */
   return _result;
  }
}

现在我们可以将流中的每一行映射到相应的结果:

public Stream<Result> parseFile(Path _file) {
  Stream<String> _stream = Files.lines(_file);
  Stream<Result> _resultStream = _stream.map(this::parse);
}

但是,流现在包含我要删除的 null 值:

parseFile(_file).filter(v -> v != null);

如果需要结果,我如何结合 map/filter 操作,正如我在 parseLine/_stream.map 中已经知道的那样?

【问题讨论】:

  • 我不明白,return Files.line(_file).map(this::parse).filter(v -&gt; v != null); 有什么问题?
  • 流将在一次运行中处理,并且仅当您使用需要完全迭代的终端操作(例如 forEach、collect、reduce)时。
  • 您关于多次通过的假设是不正确的。过滤和映射在单程中处理。 (一般来说,整个管道都是一次性处理的,除非有像排序这样的操作必须在产生任何数据之前查看所有数据。)

标签: java lambda java-8 java-stream


【解决方案1】:

正如在 cmets 中已经指出的那样,流将一次性处理,因此实际上不需要更改任何内容。你可以使用flatMap 并让parse 返回一个流:

Stream<Result> parse(String _line) {
  .. code here
  Result _result = new Result().
  if (/* line is not needed */) {
    return Stream.empty();
  } else {
    /** parse line into result */
   return Stream.of(_result);
  }
}  

public Stream<Result> parseFile(Path _file) {
  return Files.lines(_file)
              .flatMap(this::parse);
}

这样一来,您将不会有任何 null 值。

【讨论】:

  • 需要注意的是,这种方法比 map(..).filter(..) 慢很多
【解决方案2】:

Java 9 更新:

使用Stream&lt;Result&gt; 似乎是parse() 函数的错误返回类型。一个流可以包含很多很多值,因此parse() 的用户要么必须假设流中最多有一个值,要么使用collect 之类的东西来提取和使用parse() 操作的结果.如果函数和它的用法只用几行代码分开,这可能没问题,但如果距离增加,例如在完全不同的文件中进行 JUnit 测试,接口契约从返回值中看不清楚。

与其返回Stream,不如在不需要该行时返回一个空的Optional 是一个更好的接口协定。

Optional<Result> parse(String _line) {
   ... code here
   Result _result = null;
   if (/* line needed */) {
      /** parse line into result */
   }
   return Optional.ofNullable(_result);
}

不幸的是,现在_stream.map(this::parse) 返回一个Optional 值流,因此使用Java 8,您需要再次过滤和映射它与.filter(Optional::isPresent).map(Optional::get),问题是寻找可以做到这一点的解决方案“一口气”。

这个问题是 3 年前发布的。在 Java 9 中,我们现在可以选择(双关语)使用 Optional::stream 方法,因此我们可以改为:

public Stream<Result> parseFile(Path _file) {
  return Files.lines(_file)
      .map(this::parse)
      .flatMap(Optional::stream)
}

Optional 值流转换为Result 值流,不包含任何空选项。

【讨论】:

  • 在这种情况下,您要将map&amp;filter 替换为map&amp;flatMap。直接使用null,然后过滤掉没有太大区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-16
  • 2018-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
相关资源
最近更新 更多