Java 8 流条件处理

新手上路,请多包涵

我感兴趣的是将一个流分成两个或多个子流,并以不同的方式处理这些元素。例如,一个(大)文本文件可能包含类型 A 的行和类型 B 的行,在这种情况下,我想做类似的事情:

 File.lines(path)
.filter(line -> isTypeA(line))
.forEachTrue(line -> processTypeA(line))
.forEachFalse(line -> processTypeB(line))

前一个是我对情况进行抽象的尝试。实际上,我有一个非常大的文本文件,其中每一行都针对正则表达式进行测试;如果该行通过,那么它就会被处理,而如果它被拒绝,那么我想更新一个计数器。这种对拒绝字符串的进一步处理是我不简单地使用 filter 的原因。

有什么合理的方法可以用流来做到这一点,还是我必须回退到循环? (我也希望它并行运行,所以流是我的首选)。

原文由 gdiazc 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 441
2 个回答

Java 8 流并不是为支持这种操作而设计的。来自 jdk

流应该只被操作一次(调用中间或终端流操作)。这排除了,例如,“分叉”流,其中相同的源提供两个或多个管道,或同一流的多次遍历。

如果你可以将它存储在内存中,你可以使用 Collectors.partitioningBy 如果你只有两种类型并使用 Map<Boolean, List> 。否则使用 Collectors.groupingBy

原文由 Cosu 发布,翻译遵循 CC BY-SA 3.0 许可协议

简单地测试每个元素,然后采取相应的行动。

 lines.forEach(line -> {
    if (isTypeA(line)) processTypeA(line);
    else processTypeB(line);
});

此行为可以隐藏在辅助方法中:

 public static <T> Consumer<T> branch(Predicate<? super T> test,
                                     Consumer<? super T> t,
                                     Consumer<? super T> f) {
    return o -> {
        if (test.test(o)) t.accept(o);
        else f.accept(o);
    };
}

然后用法将如下所示:

 lines.forEach(branch(this::isTypeA, this::processTypeA, this::processTypeB));

切向注解

Files.lines() 方法 不会关闭底层文件, 所以必须这样使用:

 try (Stream<String> lines = Files.lines(path, encoding)) {
  lines.forEach(...);
}

Stream 类型的变量对我来说有点危险,所以我更喜欢直接管理 BufferedReader

 try (BufferedReader lines = Files.newBufferedReader(path, encoding)) {
    lines.lines().forEach(...);
}

原文由 erickson 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏