4

我需要遍历包含大约 2000 万个 Java 文件的目录层次结构。目前我正在使用FileUtils.iterateFilesApache Commons-IO。这似乎可以通过将整个列表加载到内存中来工作,这很慢(延迟应用程序启动时间)并且内存很大(大约 8GB)。我以前使用我自己的递归文件迭代器,它有同样的问题。

我一次只需要处理一个文件(或者,沿着轨道,并行处理列表前面的少数文件),因此浪费所有这些时间和内存将完整列表加载到内存中似乎有点不必要。

Java 的Iterator类允许我需要的那种最小内存占用的迭代器,但由于java.io.File该类的本机特性只提供急切初始化的数组,因此利用这些似乎非常困难。

有没有人对我如何遍历文件层次结构而不提前将其全部加载到内存有任何建议?

多亏了这个答案,我现在知道了新的 Java 7 文件 API,我认为它可以解决我的问题,但在这个阶段,Java 7 对我来说并不是一个真正的选择。

4

3 回答 3

1

由于 Java 7 NIO 不是一个选项,您可以执行“dir /B /AD”(对于 Windows)并从输出中读取文件名。如果需要,您可以将输出重定向到临时文件并从那里读取文件名。

于 2012-12-10T05:41:55.600 回答
1

好的,我最终实现了自己的迭代器来执行此操作(正如 Amir 建议的那样)。这不是微不足道的(尽管幸运的是有人已经编写了代码来扁平化迭代器),但相当简单

它仍然在内存中保存单个目录(没有后代)的完整列表,因此对于平面目录布局没有用(在这种情况下,我认为在 Java 7 之前使用纯 Java 是不走运的)但到目前为止它正在工作对我的用例来说好多了。

RecursiveFileIterable.java

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class RecursiveFileIterable implements Iterable<File> {
    private File file;

    public RecursiveFileIterable(File f) {
        file = f;
    }

    public RecursiveFileIterable(String filename) {
        this(new File(filename));
    }

    private class DirectoriesOnlyFilter implements FileFilter {
        @Override
        public boolean accept(File pathname) {
            return pathname.isDirectory();
        }

    }

    private class NoDirectoriesFilter implements FileFilter {
        @Override
        public boolean accept(File pathname) {
            return !pathname.isDirectory();
        }
    }

    @Override
    public Iterator<File> iterator() {
        List<File> normFiles = Arrays.asList(file
                .listFiles(new NoDirectoriesFilter()));
        ArrayList<Iterable<File>> pendingIterables = new ArrayList<Iterable<File>>();
        pendingIterables.add(normFiles);

        File[] subdirs = file.listFiles(new DirectoriesOnlyFilter());
        for (File sd : subdirs)
            pendingIterables.add(new RecursiveFileIterable(sd));

        return new FlattenIterable<File>(pendingIterables).iterator();

    }

}

FlattenIterable.java

// from http://langexplr.blogspot.com.au/2007/12/combining-iterators-in-java.html

import java.util.Iterator;

public class FlattenIterable<T> implements Iterable<T> {
    private Iterable<Iterable<T>> iterable;

    public FlattenIterable(Iterable<Iterable<T>> iterable) {
        this.iterable = iterable;
    }

    public Iterator<T> iterator() {
        return new FlattenIterator<T>(iterable.iterator());
    }

    static class FlattenIterator<T> implements Iterator<T> {
        private Iterator<Iterable<T>> iterator;
        private Iterator<T> currentIterator;

        public FlattenIterator(Iterator<Iterable<T>> iterator) {
            this.iterator = iterator;
            currentIterator = null;
        }

        public boolean hasNext() {
            boolean hasNext = true;
            if (currentIterator == null) {
                if (iterator.hasNext()) {
                    currentIterator = iterator.next().iterator();
                } else {
                    return false;
                }
            }

            while (!currentIterator.hasNext() && iterator.hasNext()) {
                currentIterator = iterator.next().iterator();
            }

            return currentIterator.hasNext();
        }

        public T next() {
            return currentIterator.next();
        }

        public void remove() {
        }
    }
}
于 2012-12-10T23:21:45.300 回答
1

我知道这不是您问题的严格答案,但是您能否不重新组织目录树以使用更多级别的目录,以便每个目录包含更少的文件?

于 2012-12-10T07:05:22.127 回答