以最少的内存使用量遍历Java中的许多文件

时间:2012-12-10 05:27:16

标签: java file file-io iterator iteration

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

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

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

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

感谢this answer我现在知道新的Java 7文件API,我认为它可以解决我的问题,但Java 7在这个阶段对我来说不是一个真正的选择。

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

好的,我最终实现了自己的迭代器来做到这一点(正如Amir建议的那样)。这并不是微不足道的(幸运的是someone already wrote code to flatten iterators),但是相当简单

它仍然保存了内存中单个目录(没有后代)的完整列表,因此对于平面目录布局没有用处(在这种情况下,我认为在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() {
        }
    }
}