Java非递归文件系统走路

时间:2012-03-07 17:27:53

标签: java

我需要创建使用非递归遍历文件系统的应用程序并打印出一定深度的文件。 我有什么:

public void putFileToQueue() throws IOException, InterruptedException {
File root = new File(rootPath).getAbsoluteFile();
checkFile(root, depth);
    Queue<DepthControl> queue = new ArrayDeque<DepthControl>();
    DepthControl e = new DepthControl(0, root);
    do {
        root = e.getFileName();

        if (root.isDirectory()) {
            File[] files = root.listFiles();

            if (files != null)
                for (File file : files) {

                    if (e.getDepth() + 1 <= depth && file.isDirectory()) {
                        queue.offer(new DepthControl(e.getDepth() + 1,file));
                    }

                    if (file.getName().contains(mask)) {
                        if (e.getDepth() == depth) {
                            System.out.println(Thread.currentThread().getName()
                                    + " putting in queue: "
                                    + file.getAbsolutePath());
                        }
                    }
                }
                } 
        e = queue.poll();
    } while (e != null);
}

辅助班

public class DepthControl {

    private int depth;
    private File file;

    public DepthControl(int depth, File file) {

        this.depth = depth;
        this.file = file;
    }

    public File getFileName() {
        return file;
    }

    public int getDepth() {
        return depth;
    }
}

我收到回答,由于广度优先搜索(希望正确翻译),该程序使用额外的内存。我有O(k ^ n),其中k - 子目录的平均数量,n - 深度。这个程序可以用O(k * n)轻松完成。请帮我修改算法。

6 个答案:

答案 0 :(得分:4)

我认为这应该可以完成这项任务并且更简单一些。它只是跟踪下一级别的文件,扩展它们,然后重复该过程。算法本身跟踪深度,因此不需要额外的类。

// start in home directory.
File root = new File(System.getProperty("user.dir"));

List<File> expand = new LinkedList<File>();
expand.add(root);

for (int depth = 0; depth < 10; depth++) {
    File[] expandCopy = expand.toArray(new File[expand.size()]);
    expand.clear();
    for (File file : expandCopy) {
        System.out.println(depth + " " + file);
        if (file.isDirectory()) {
            expand.addAll(Arrays.asList(file.listFiles()));
        }
    }
}

答案 1 :(得分:1)

为了避免在走树时递归,基本上有两种选择:

  1. 使用“工作清单”(类似于上述内容)来跟踪要完成的工作。在检查每个项目时,作为结果“发现”的新工作项被添加到工作列表中(可以是FIFO,LIFO或随机顺序 - 在概念上无关紧要,尽管它通常会影响“参考地点”性能)。
  2. 使用堆栈/“下推列表”,以便基本上模拟递归方案。
  3. 对于#2,你必须编写一个属于状态机的算法,在每一步之后返回堆栈以确定下一步该做什么。树步行的堆栈条目基本上包含当前树节点和要检查的下一个子节点的子列表中的索引。

答案 2 :(得分:0)

假设您想限制使用的空间量,并且:

  • 您可以假设文件/目录列表在遍历过程中是静态的,AND
  • 您可以假设给定目录中的文件/目录列表始终以相同的顺序返回
  • 您可以访问当前目录的父级

然后,您只能使用最后访问的节点的信息遍历目录。具体来说,就像

那样
1. Keep track of the last Entry (directory or file) visited
2. Keep track of the current directory
3. Get a list of files in the current directory
4. Find the index of the last Entry visited in the list of files
5. If lastVisited is the last Entry in the current directory, 
5.1.1 If current directory == start directory, we're done
5.1.2 Otherwise, lastVisited = the current directory and current directory is the parent directory
5.2. Otherwise, visit the element after lastVisited and set lastVisited to that element
6. Repeat from step 3

如果可以,我会尝试写一些代码来表明我明天的意思......但我现在还没有时间。

注意:这不是一种很好的方式来遍历目录结构......它只是一种可能的方式。在正常的盒子之外,可能是有充分理由的。

你不得不原谅我没有用Java提供示例代码,我没有时间在那个atm上工作。在Tcl中完成它对我来说更快,并且它不应该太难理解。那就是说:

proc getFiles {dir} {
    set result {}
    foreach entry [glob -tails -directory $dir * .*] {
        if { $entry != "." && $entry != ".." } {
            lappend result [file join $dir $entry]
        }
    }
    return [lsort $result]
}

proc listdir {startDir} {
    if {! ([file exists $startDir] && [file isdirectory $startDir])} {
        error "File '$startDir' either doesn't exist or isnt a directory"
    }
    set result {}
    set startDir [file normalize $startDir]
    set currDir $startDir
    set currFile {}

    set fileList [getFiles $currDir]
    for {set i 0} {$i < 1000} {incr i} { # use for to avoid infinate loop
        set index [expr {1 + ({} == $currFile ? -1 : [lsearch $fileList $currFile])}]
        if {$index < ([llength $fileList])} {
            set currFile [lindex $fileList $index]
            lappend result $currFile
            if { [file isdirectory $currFile] } {
                set currDir $currFile
                set fileList [getFiles $currDir]
                set currFile {}
            }
        } else {
            # at last entry in the dir, move up one dir
            if {$currDir == $startDir} {
                # at the starting directory, we're done
                return $result
            }
            set currFile $currDir
            set currDir [file dirname $currDir]
            set fileList [getFiles $currDir]
        }
    }
}

puts "Files:\n\t[join [listdir [lindex $argv 0]] \n\t]"

然后,运行它:

VirtualBox:~/Programming/temp$ ./dirlist.tcl /usr/share/gnome-media/icons/hicolor
Files:
    /usr/share/gnome-media/icons/hicolor/16x16
    /usr/share/gnome-media/icons/hicolor/16x16/status
    /usr/share/gnome-media/icons/hicolor/16x16/status/audio-input-microphone-high.png
    /usr/share/gnome-media/icons/hicolor/16x16/status/audio-input-microphone-low.png
    /usr/share/gnome-media/icons/hicolor/16x16/status/audio-input-microphone-medium.png
    /usr/share/gnome-media/icons/hicolor/16x16/status/audio-input-microphone-muted.png
    /usr/share/gnome-media/icons/hicolor/22x22
    [snip]
    /usr/share/gnome-media/icons/hicolor/48x48/devices/audio-subwoofer-testing.svg
    /usr/share/gnome-media/icons/hicolor/48x48/devices/audio-subwoofer.svg
    /usr/share/gnome-media/icons/hicolor/scalable
    /usr/share/gnome-media/icons/hicolor/scalable/status
    /usr/share/gnome-media/icons/hicolor/scalable/status/audio-input-microphone-high.svg
    /usr/share/gnome-media/icons/hicolor/scalable/status/audio-input-microphone-low.svg
    /usr/share/gnome-media/icons/hicolor/scalable/status/audio-input-microphone-medium.svg
    /usr/share/gnome-media/icons/hicolor/scalable/status/audio-input-microphone-muted.svg

答案 3 :(得分:0)

如果你正在使用Java 7,那么有一种非常优雅的方法来处理文件树。您需要确认它是否符合您的递归需求。

import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import static java.nio.file.FileVisitResult.*;

public  class myFinder extends SimpleFileVisitor<Path> {

public FileVisitResult visitFile(Path file, BasicFileAttributes attr) { }
public FileVisitResult postVisitDirectory(Path dir, IOException exc) { }
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { }
public FileVisitResult visitFileFailed(Path file, IOException exc) { }


<snip>

}

本质上,它执行树的深度优先行走,并在进入/退出目录时调用某些方法,并且当它进入&#34;访问&#34;一个文件。

我认为这是针对Java 7的。

http://docs.oracle.com/javase/tutorial/essential/io/walk.html

答案 4 :(得分:0)

而且 - 当然 - 总是有多线程选项来避免递归。

  1. 创建文件队列。
  2. 如果是文件,请将其添加到队列中。
  3. 如果是文件夹,则启动一个新线程,列出其中也提供此队列的文件。
  4. 获取下一个项目。
  5. 根据需要从2重复。
  6. 显然,这可能无法以可预测的顺序列出文件。

答案 5 :(得分:0)

在Java 8中,您可以使用stream,Files.walk和maxDepth为1

    try (Stream<Path> walk = Files.walk(Paths.get(filePath), 1)) {

        List<String> result = walk.filter(Files::isRegularFile)
                .map(Path::toString).collect(Collectors.toList());

        result.forEach(System.out::println);

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