3

简而言之,情况如下:

for(element : array)
{
   execute something that adds new elements to the array;
}

很明显,新添加的元素不会被处理。是否有一种技术可以处理每个元素?

这是这个问题的一个例子:我想,给定一个文件夹,遍历它的深度并将所有文件 - 无论是在它的孩子下还是在它的孩子的孩子下 - 直接在给定的文件夹下。之后删除所有空文件夹:

Parent Folder
 - Level1Folder1
    - file1_1
 - Level1Folder2
    - Level2Folder1
        - file2_1
        - file2_2
    - file1_2

会变成:

Parent Folder
 - file1_1
 - file1_2
 - file2_1
 - file2_2

这是我的代码的药水:

public static void moveUpOneFolder(Path parent) {

    try (DirectoryStream<Path> ds1 = Files.newDirectoryStream(parent)) {
        for (Path p1 : ds1) {
            //if this node is a dir, traverse its content
            if (Files.isDirectory(p1)) {
                moveUpOneFolder(p1);
            }
            //if this node is a file, move it up one level
            else {
                Path newFileName = parent.getParent().resolve(p1.getName(p1.getNameCount() - 2) + "_" + p1.getFileName());
                Files.move(p1, newFileName);
            }
            Files.delete(p1);
        }

    } catch (IOException e) {
        e.printStackTrace();
    }
}

此递归不起作用,因为当执行到达 Level2Folder1 时,它将 file2_1 和 file 2_2 移动到 Level1Folder2,然后继续将 file1_2 移动到父文件夹,忽略 file2_1 和 file2_2,新添加到文件夹的元素。发生这种情况是因为 ds1 已经为 for 循环初始化,新元素未添加到此数组/流中,因此被忽略。

我想这对于经验丰富的编码人员来说并不难,但我真的被困住了。:-)

4

4 回答 4

6

Java 集合通过抛出ConcurrentModificationException. ;)

在这种特殊情况下,我倾向于推荐队列类型的结构;而不是使用for循环,重复出队元素并将更多元素添加到队列的后面。

于 2013-08-26T22:48:38.273 回答
2

为什么需要一次将文件向上移动一个目录?我认为这是您问题的根源,您正在创建一个过于复杂的解决方案。相反,如果您以稍微不同的方式解决问题,并将行为的组成部分拆分为多个部分,则可以避免遇到的问题。在伪java代码中:

public void moveAllFiles(File fromDir, File toDir) {
  for(File f : getAllFilesAndDirs(fromDir)) {
    if(f.isDirectory()) {
      moveAllFiles(f, toDir);
      f.delete();
    } else {
      move(f, toDir);
    }
  }
}

注意几点:

  1. 我们在逻辑上将 and 分开fromDirtoDir不需要要求它们位于相同的位置,这让我们可以递归地使用调用。
  2. 我们立即得到所有的孩子,fromDir只要没有其他程序修改目录,我们就不再关心以后目录是否发生变化,避免了并发修改的问题。
  3. 通过使用递归,我们可以相信我们正在查看文件或(现在)空目录。
于 2013-08-26T23:19:24.157 回答
1

您不需要在向数组添加元素时迭代数组。那会抛出各种各样的ConcurrentModificationException.

使用 Java 的 NIO,扁平化目录结构并不难。将所有文件复制到一个临时目录,然后删除。然后从该临时目录复制回您的根目录。

final Path root = Paths.get("/root");
final Path temp = Files.createTempDirectory(null);
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        if (attrs.isRegularFile()) {
            Files.copy(file, temp.resolve(file.getFileName()));
            Files.delete(file);
        }
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        if (!dir.equals(root))
            Files.delete(dir);
        return FileVisitResult.CONTINUE;
    }           
});

Files.walkFileTree(temp, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        if (attrs.isRegularFile()) {
            Files.copy(file, root.resolve(file.getFileName()));
            Files.delete(file);
        }
        return FileVisitResult.CONTINUE;
    }
});

这将使您的目录结构变平,只保留文件并丢弃目录。

于 2013-08-26T22:54:45.923 回答
1

我建议使用 aStack或 aQueue而不是数组。这意味着您继续循环,直到数据结构为空。在循环期间,您添加需要处理的元素(即包含应该移动的文件的目录)。

于 2013-08-26T23:12:14.400 回答