Files.delete 和 Files.deleteIfExists 有点奇怪的行为

A bit strange behaviour of Files.delete and Files.deleteIfExists

我得到这样的代码:

paths.forEach(folderPath -> {
        Path to = folderPath.getRoot().resolve(folderPath.getParent().subpath(0, folderPath.getNameCount() - 1)); // До имени (исключительно)
        try {
            Files.list(folderPath).forEach(filePath -> {
                try { Files.move(filePath, to.resolve(filePath.getFileName()), StandardCopyOption.ATOMIC_MOVE); }
                catch (IOException e) { processException(e); }
            });
            if (Files.list(folderPath).count() == 0)
                Files.deleteIfExists(folderPath); // this call
        } catch (IOException e) { processException(e); }
    });

调用删除方法后,我锁定了我的空目录(调用后立即检查),但直到应用程序关闭后才删除。我觉得有点奇怪,但想知道为什么会这样。

(我用Windows10)

来自 Files.list(Path) 的文档:

This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directory is closed promptly after the stream's operations have completed.

您没有这样做,因此 Files.deleteIfExists(…) 的以下部分适用:

On some operating systems it may not be possible to remove a file when it is open and in use by this Java virtual machine or other programs.

你应该使用

paths.forEach(folderPath -> {
    Path to = folderPath.getParent();
    try {
        try(Stream<Path> files = Files.list(folderPath)) {
            files.forEach(filePath -> {
                try{Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);}
                catch (IOException e) { processException(e); }
            });
        }
        try {
            Files.deleteIfExists(folderPath);
        } catch(DirectoryNotEmptyException ex) {
            // may happen as you continue when Files.move fails,
            // but you already reported the original exception then
        }
    } catch (IOException e) { processException(e); }
});

这会在尝试删除目录之前关闭文件流。请注意,第二个流操作已被删除,这种预检查很浪费,当所有 move 操作成功时应该不需要。但是 如果 一些其他应用程序同时插入一个新文件,不能保证它不会在您的 Files.list(folderPath).count() == 0 检查和随后的 deleteIfExists 调用之间发生。

更简洁的解决方案是记住 move 失败的时间。当没有 move 失败时,仍不为空的目录应被视为错误情况,应像任何其他错误一样报告,例如

paths.forEach(folderPath -> {
    Path to = folderPath.getParent();
    try {
        boolean allMovesSucceeded;
        try(Stream<Path> files = Files.list(folderPath)) {
          allMovesSucceeded = files
            .map(filePath -> {
                try {
                    Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);
                    return true;
                }
                catch(IOException e) { processException(e); return false; }
            }).reduce(Boolean.TRUE, Boolean::logicalAnd);

        }
        if(allMovesSucceeded) Files.deleteIfExists(folderPath);
    } catch (IOException e) { processException(e); }
});