Java 8 流条件处理

2022-09-01 03:47:31

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

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

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

是否有任何合理的方法可以对流执行此操作,或者我是否必须回退到循环?(我希望它也并行运行,所以流是我的首选)。


答案 1

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

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

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


答案 2

只需测试每个元素,并采取相应的行动。

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(...);
}

类型的变量为我抛出了一个危险信号,所以我更喜欢直接管理一个:StreamBufferedReader

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

推荐