如何在功能上处理拆分流

时间:2017-12-15 19:23:49

标签: java java-8 functional-programming java-stream

鉴于以下代码,我如何将其简化为单一的功能线?

    // DELETE CSV TEMP FILES
    final Map<Boolean, List<File>> deleteResults = Stream.of(tmpDir.listFiles())
            .filter(tempFile -> tempFile.getName().endsWith(".csv"))
            .collect(Collectors.partitioningBy(File::delete));

    // LOG SUCCESSES AND FAILURES
    deleteResults.entrySet().forEach(entry -> {
        if (entry.getKey() && !entry.getValue().isEmpty()) {
            LOGGER.debug("deleted temporary files, {}",
                    entry.getValue().stream().map(File::getAbsolutePath).collect(Collectors.joining(",")));
        } else if (!entry.getValue().isEmpty()) {
            LOGGER.debug("failed to delete temporary files, {}",
                    entry.getValue().stream().map(File::getAbsolutePath).collect(Collectors.joining(",")));
        }
    });

这是我遇到的常见模式,我有一些东西,我想过滤这个流,根据该过滤器创建两个流,然后我可以做一件事来流A和另一件事来流B.这是一种反模式,还是以某种方式支持?

2 个答案:

答案 0 :(得分:5)

如果您特别不希望显式变量引用临时地图,那么您可以将操作链接起来:

.collect(Collectors.partitioningBy(File::delete))
.forEach((del, files) -> {
    if (del) {
        LOGGER.debug(... files.stream()...);
    } else {
        LOGGER.debug(... files.stream()...);
    });

答案 1 :(得分:2)

如果要将这两个类别的所有文件一起记录,则无法将它们收集到包含它们的数据结构中,直到所有元素都已知。不过,您可以简化代码:

Stream.of(tmpDir.listFiles())
      .filter(tempFile -> tempFile.getName().endsWith(".csv"))
      .collect(Collectors.partitioningBy(File::delete,
          Collectors.mapping(File::getAbsolutePath, Collectors.joining(","))))
.forEach((success, files) -> {
    if (!files.isEmpty()) {
        LOGGER.debug(success? "deleted temporary files, {}":
                              "failed to delete temporary files, {}",
                     files);
    }
});

这不会将文件收集到List中,而是收集到预期的String中以用于后续的日志记录操作。对于这两种情况,日志记录操作也是相同的,但仅在消息中有所不同。

然而,最有趣的是为什么删除文件失败,而boolean没有说明。从Java 7开始,nio包提供了更好的选择:

创建帮助方法

public static String deleteWithReason(Path p) {
    String problem;
    IOException ioEx;

    try {
        Files.delete(p);
        return "";
    }
    catch(FileSystemException ex) {
        problem = ex.getReason();
        ioEx = ex;
    }
    catch(IOException ex) {
        ioEx = ex;
        problem = null;
    }
    return problem!=null? problem.replaceAll("\\.?\\R", ""): ioEx.getClass().getName();
}

并像

一样使用它
Files.list(tmpDir.toPath())
      .filter(tempFile -> tempFile.getFileName().toString().endsWith(".csv"))
      .collect(Collectors.groupingBy(YourClass::deleteWithReason,
          Collectors.mapping(p -> p.toAbsolutePath().toString(), Collectors.joining(","))))
.forEach((failure, files) -> 
    LOGGER.debug(failure.isEmpty()? "deleted temporary files, {}":
                           "failed to delete temporary files, "+failure+ ", {}",
                 files)
);

如果您想以这种方式调用它,那么缺点是不会为所有失败的文件生成单个条目,如果它们有不同的失败原因。但是,如果您想要记录它们并且无法删除它们,那么这显然是不可避免的。

请注意,如果您要排除故障中的“被其他人同时删除”,您只需使用Files.deleteIfExists(p)代替Files.delete(p),并且已被删除将被视为成功。