循环通过动态大小的数组

时间:2013-08-26 22:44:59

标签: java

简而言之就是情况:

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和文件2_2移动到Level1Folder2,然后继续将file1_2移动到父文件夹,忽略file2_1和file2_2,即文件夹中新添加的元素。发生这种情况是因为ds1已经为for循环初始化,新元素没有添加到此数组/流中,因此被忽略。

我想这对经验丰富的程序员来说并不困难,但我真的被卡住了。 : - )

4 个答案:

答案 0 :(得分:6)

Java Collections通过抛出ConcurrentModificationException来拒绝此问题。 ;)

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

答案 1 :(得分: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. 我们逻辑上将fromDirtoDir分开,没有必要要求它们位于同一位置,这样我们就可以递归地使用该调用。
  2. 我们立即得到fromDir的所有孩子,只要没有其他程序修改目录,我们就不再关心目录是否会更改,从而避免了并发修改问题。
  3. 通过使用递归,我们可以相信我们正在查看文件或(现在)空目录。

答案 2 :(得分: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;
    }
});

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

答案 3 :(得分:1)

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