Java 8:一次应用流映射和过滤

时间:2014-11-06 12:37:01

标签: java lambda java-8 java-stream

我正在为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);

我如何组合地图/过滤器操作,正如我在parseLine/_stream.map中已经知道的那样,是否需要结果?

2 个答案:

答案 0 :(得分:11)

正如评论中已经指出的那样,流将在一次通过中处理,因此实际上不需要改变任何东西。对于它的价值,你可以使用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个值。

答案 1 :(得分:3)

更新Java 9:

使用Stream<Result>似乎是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值流,而不包含任何空选项。