4

Javaで約2,000万個のファイルを含むディレクトリ階層をトラバースする必要があります。現在FileUtils.iterateFiles、ApacheCommons-IOから使用しています。これは、リスト全体をメモリにロードすることで機能するようです。これは、低速(アプリケーションの起動時間を遅らせる)であり、メモリを大量に消費します(約8GB)。以前は、同じ問題が発生した独自の再帰ファイルイテレータを使用していました。

一度に1つのファイルを処理するだけで済みます(または、リストの先頭から並行して少数のファイルを処理する必要があります)。そのため、この時間とメモリを無駄にせずに、完全なリストをメモリにロードする必要はありません。

JavaのIteratorクラスでは、必要な種類の最小メモリフットプリントのイテレータを使用できますが、java.io.Fileクラスのネイティブ機能は熱心に初期化された配列しか提供しないため、これらを利用するのは非常に難しいようです。

事前にすべてをメモリにロードせずにファイル階層をトラバースする方法について誰かが提案を持っていますか?

この回答のおかげで、新しいJava 7ファイルAPIを認識しました。これで問題が解決すると思いますが、この段階ではJava7は実際にはオプションではありません。

4

3 に答える 3

1

Java 7 NIOはオプションではないため、「dir / B / AD」(Windowsの場合)を実行して、出力からファイル名を読み取ることができます。必要に応じて、出力を一時ファイルにリダイレクトし、そこからファイル名を読み取ることができます。

于 2012-12-10T05:41:55.600 に答える
1

厳密にはあなたの質問に対する答えではないことは知っていますが、ディレクトリツリーを再編成して、より多くのレベルのディレクトリを使用し、各ディレクトリに含まれるファイルの数を減らすことはできませんか?

于 2012-12-10T07:05:22.127 に答える
1

OK、これを行うために独自のイテレータを実装することになりました(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 に答える