Рекурсивно перечислять файлы в Java



8 Answers

Java 8 обеспечивает хороший поток для обработки всех файлов в дереве.

Files.walk(Paths.get(path))
        .filter(Files::isRegularFile)
        .forEach(System.out::println);

Это обеспечивает естественный способ перемещения файлов. Поскольку это поток, вы можете делать все приятные потоковые операции с результатом, таким как ограничение, группировка, сопоставление, выход с раннего начала и т. Д.

ОБНОВЛЕНИЕ : я мог бы указать, что есть также Files.find который берет BiPredicate который может быть более эффективным, если вам нужно проверить атрибуты файла.

Files.find(Paths.get(path),
           Integer.MAX_VALUE,
           (filePath, fileAttr) -> fileAttr.isRegularFile())
        .forEach(System.out::println);

Обратите внимание, что, хотя JavaDoc ускользает от того, что этот метод может быть более эффективным, чем Files.walk он фактически идентичен, разница в производительности может наблюдаться, если вы также извлекаете атрибуты файлов в свой фильтр. В конце концов, если вам нужно фильтровать атрибуты, используйте Files.find , иначе используйте Files.walk , в основном потому, что есть перегрузки, и это более удобно.

ИСПЫТАНИЯ : В соответствии с просьбой я дал сравнение эффективности многих ответов. Ознакомьтесь с проектом Github, который содержит результаты и тестовый пример .

Question

Как я рекурсивно перечислить все файлы в каталоге на Java? Предоставляет ли инфраструктура какую-либо полезность?

Я видел много хакерских реализаций. Но ни один из рамок или nio




Я думаю, что это должно сделать работу:

File dir = new File(dirname);
String[] files = dir.list();

Таким образом, у вас есть файлы и директории. Теперь используйте рекурсию и сделайте то же самое для dirs (класс File имеет метод isDirectory() ).




Я бы пошел с чем-то вроде:

public void list(File file) {
    System.out.println(file.getName());
    File[] children = file.listFiles();
    for (File child : children) {
        list(child);
    }
}

System.out.println просто указывает, чтобы что-то делать с файлом. нет необходимости различать файлы и каталоги, поскольку обычный файл будет просто иметь ноль детей.




Этот код готов к запуску

public static void main(String... args) {
    File[] files = new File("C:/").listFiles();
    if (files != null) 
       getFile(files);
}

public static void getFile(File[] files) {
    for (File file : files) {
        if (file.isDirectory()) {
            getFile(file.listFiles());
        } else {
            System.out.println("File: " + file.toString());
        }
    }
}



Нерекурсивный BFS с одним списком (конкретный пример - поиск файлов * .eml):

    final FileFilter filter = new FileFilter() {
        @Override
        public boolean accept(File file) {
            return file.isDirectory() || file.getName().endsWith(".eml");
        }
    };

    // BFS recursive search
    List<File> queue = new LinkedList<File>();
    queue.addAll(Arrays.asList(dir.listFiles(filter)));

    for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
        File file = itr.next();
        if (file.isDirectory()) {
            itr.remove();
            for (File f: file.listFiles(filter)) itr.add(f);
        }
    }



Здесь простое, но отлично работающее решение с использованием recursion :

public static List<Path> listFiles(String rootDirectory)
{
    List<Path> files = new ArrayList<>();
    listFiles(rootDirectory, files);

    return files;
}

private static void listFiles(String path, List<Path> collectedFiles)
{
    File root = new File(path);
    File[] files = root.listFiles();

    if (files == null)
    {
        return;
    }

    for (File file : files)
    {
        if (file.isDirectory())
        {
            listFiles(file.getAbsolutePath(), collectedFiles);
        } else
        {
            collectedFiles.add(file.toPath());
        }
    }
}



Java 7 будет иметь Files.walkFileTree :

Если вы указываете отправную точку и посетителя файла, она будет вызывать различные методы для посетителя файла, когда он просматривает файл в дереве файлов. Мы ожидаем, что люди будут использовать это, если они разрабатывают рекурсивную копию, рекурсивное перемещение, рекурсивное удаление или рекурсивную операцию, которая устанавливает разрешения или выполняет другую операцию для каждого из файлов.

В настоящее время имеется целый учебник Oracle по этому вопросу .




Помимо рекурсивного обхода можно использовать подход, основанный на посетителях.

Ниже код использует подход на основе Visitor для обхода. Ожидается, что вход в программу является корневым каталогом для перемещения.

public interface Visitor {
    void visit(DirElement d);
    void visit(FileElement f);
}

public abstract class Element {
    protected File rootPath;
    abstract void accept(Visitor v);

    @Override
    public String toString() {
        return rootPath.getAbsolutePath();
    }
}

public class FileElement extends Element {
    FileElement(final String path) {
        rootPath = new File(path);
    }

    @Override
    void accept(final Visitor v) {
        v.visit(this);
    }
}

public class DirElement extends Element implements Iterable<Element> {
    private final List<Element> elemList;
    DirElement(final String path) {
        elemList = new ArrayList<Element>();
        rootPath = new File(path);
        for (File f : rootPath.listFiles()) {
            if (f.isDirectory()) {
                elemList.add(new DirElement(f.getAbsolutePath()));
            } else if (f.isFile()) {
                elemList.add(new FileElement(f.getAbsolutePath()));
            }
        }
    }

    @Override
    void accept(final Visitor v) {
        v.visit(this);
    }

    public Iterator<Element> iterator() {
        return elemList.iterator();
    }
}

public class ElementWalker {
    private final String rootDir;
    ElementWalker(final String dir) {
        rootDir = dir;
    }

    private void traverse() {
        Element d = new DirElement(rootDir);
        d.accept(new Walker());
    }

    public static void main(final String[] args) {
        ElementWalker t = new ElementWalker("C:\\temp");
        t.traverse();
    }

    private class Walker implements Visitor {
        public void visit(final DirElement d) {
            System.out.println(d);
            for(Element e:d) {
                e.accept(this);
            }
        }

        public void visit(final FileElement f) {
            System.out.println(f);
        }
    }
}



Я предпочитаю использовать очередь для рекурсии для такого простого простого обращения:

List<File> allFiles = new ArrayList<File>();
Queue<File> dirs = new LinkedList<File>();
dirs.add(new File("/start/dir/"));
while (!dirs.isEmpty()) {
  for (File f : dirs.poll().listFiles()) {
    if (f.isDirectory()) {
      dirs.add(f);
    } else if (f.isFile()) {
      allFiles.add(f);
    }
  }
}



Related