使用并行流有效地处理文件比较

时间:2017-07-14 00:33:46

标签: parallel-processing java-8 java-stream

所以,我有多个txt文件,比如txt1,txt2,...,每行有一些4到22个字符的文本,我有另一个具有相似值的txt文件,比如说bigText。目标是检查bigTxt中出现在任何txt文件中某处的所有值并输出这些值(我们确保bigTxt中的任何一行都在任何一个txt文件,与该行匹配只发生一次)。到目前为止,我所拥有的最佳解决方案是有效的,但效率稍低。基本上,它看起来像这样:

txtFiles.parallelStream().forEach(file->{
   List<String> txtList = listOfLines of this txtFile;
   streamOfLinesOfBigTxt.forEach(line->{
         if(txtList.contains(line)){
            System.out.println(line);
            //it'd be great if we could just stop this forEach loop here
            //but that seems hardish
         }
   });
});

(注意:我尝试使用Honza&#34;错误的想法&#34;解决方案来解决forEach:Break or return from Java 8 stream forEach?但这必须做一些不是我的事情想要,因为它实际上使代码通常有点慢或大致相同) 这个问题的一个小问题是,即使一个文件找到了bigTxt文件和其他txt文件之间的一条线的匹配,其他txt文件仍然会尝试搜索该行的检查(即使我们&已经找到了一场比赛并且足够了。我试图阻止这一点的事情是首先迭代bigTxt行(不是并行,但是并行浏览每个txt文件)并使用java&#39; s anyMatch我得到了一个&#34;流已被修改或关闭&#34;我后面理解的错误类型是因为anyMatch正在终止。因此,在其中一个txt文件的其中一行上只调用一次anyMatch之后,该流将不再可供我稍后处理。我无法想出正确使用findAny的方法,我也不认为allMatch是我想要的,因为bigTxt的所有值都不一定在其中一个txt文件。任何(并行)解决方案(甚至不严格包括Java 8中的内容)都是受欢迎的。谢谢。

1 个答案:

答案 0 :(得分:3)

如果streamOfLinesOfBigTxtStream,则您在问题中发布的代码会出现同样的错误,因为您尝试使用外部流forEach多次处理该流。目前尚不清楚为什么你没有注意到这一点,但也许你总是在程序开始处理第二个文件之前就停止了它?毕竟,为大文件的每一行线性搜索List行所需的时间与两行数的乘积成比例。

当你说,你想“检查任何txt文件中某处出现的bigTxt中的所有值并输出这些值”时,你可以直截了当地做到这一点:

Files.lines(Paths.get(bigFileLocation))
     .filter(line -> txtFiles.stream()
                 .flatMap(path -> {
                         try { return Files.lines(Paths.get(path)); }
                         catch (IOException ex) { throw new UncheckedIOException(ex); }
                     })
                 .anyMatch(Predicate.isEqual(line)) )
    .forEach(System.out::println);

这会造成短路,但仍然存在与n×m一致的处理时间问题。更糟糕的是,它会重复打开并反复读取txtfiles。

如果您想避免这种情况,将数据存储在RAM中是不可避免的。如果存储它们,您可以选择一个支持优于线性查找的存储:

Set<String> matchLines = txtFiles.stream()
    .flatMap(path -> {
        try { return Files.lines(Paths.get(path)); }
        catch (IOException ex) { throw new UncheckedIOException(ex); }
    })
    .collect(Collectors.toSet());

Files.lines(Paths.get(bigFileLocation))
     .filter(matchLines::contains)
     .forEach(System.out::println);

现在,它的执行时间与所有文件的行数而不是产品总和成比例。但它需要临时存储txtFiles的所有不同行。

如果大文件的明显行数少于其他文件并且顺序无关紧要,则将大文件的行存储在一个集合中,然后检查txtFiles的行

Set<String> matchLines
    = Files.lines(Paths.get(bigFileLocation)).collect(Collectors.toSet());

txtFiles.stream()
        .flatMap(path -> {
            try { return Files.lines(Paths.get(path)); }
            catch (IOException ex) { throw new UncheckedIOException(ex); }
        })
        .filter(matchLines::contains)
        .forEach(System.out::println);

这依赖于所有匹配行在所有这些文本文件中都是唯一的属性,正如您在问题中所述。

我不认为,这里的并行处理会带来任何好处,因为I / O速度将主导执行。