web-dev-qa-db-ja.com

Javaを使用してディレクトリ内のファイル数をカウントする

Java?を使用してディレクトリ内のファイル数をカウントするにはどうすればよいですか?簡単にするために、ディレクトリにサブディレクトリがないと仮定します。

私は標準的な方法を知っています:

new File(<directory path>).listFiles().length

ただし、これはディレクトリ内のすべてのファイルを効率的に処理するため、ファイル数が多い場合は時間がかかる場合があります。また、ディレクトリ内の実際のファイルについては、その数が固定された大きな数(5000など)を超えない限り気にしません。

私は推測していますが、ディレクトリ(またはUnixの場合はiノード)に含まれるファイルの数は保存されませんか?その数値をファイルシステムからすぐに取得できれば、はるかに高速になります。バックエンドが実際の処理を開始する前に、Tomcatサーバー上のすべてのHTTP要求に対してこのチェックを行う必要があります。したがって、速度は非常に重要です。

ディレクトリをクリアするために、時々デーモンを実行できました。私はそれを知っているので、その解決策を私に教えないでください。

60
euphoria83

これはアプリケーションには適切ではないかもしれませんが、常にネイティブコール(jniまたは jna を使用)を試みるか、プラットフォーム固有のコマンドを実行してlist()に戻る前に出力を読み取ることができます。長さ。 * nixでは、ls -1a | wc -l(注-最初のコマンドはダッシュ1つ、2番目はダッシュ小文字Lです)。 Windowsで何が正しいかわからない-おそらくdirだけで概要を探してください。

このようなことに悩む前に、非常に多くのファイルを含むディレクトリを作成し、list()。lengthが実際に時間がかかるかどうかを確認することを強くお勧めします。 this blogger が示唆しているように、これに汗をかいたくないかもしれません。

たぶん、Varkhanの答えを自分で行くと思います。

12
Marty Lamb

Ah ... Javaで簡単なメソッドを持たない理由は、ファイルストレージの抽象化です。一部のファイルシステムでは、ディレクトリ内のファイルの数がすぐに利用できない場合があります。まったく意味がないかもしれません(たとえば、分散P2Pファイルシステム、リンクされたリストとしてファイルリストを保存するfs、またはデータベースバックアップファイルシステムを参照してください)。

new File(<directory path>).list().length

おそらくあなたの最善策です。

79
Varkhan

Java 8なので、3行でそれを行うことができます。

try (Stream<Path> files = Files.list(Paths.get("your/path/here"))) {
    long count = files.count();
}

5000の子ノードとiノードの側面に関して:

このメソッドはエントリを繰り返し処理しますが、Varkhanが示唆したように、おそらくJNIまたは直接のシステムコマンドコールで遊ぶ以外にもっと良い方法はありません。

ただし、これについて少し掘り下げてみましょう。

JDK8ソースを見ると、Files.listは、Files.newDirectoryStreamに委任するFileSystemProvider.newDirectoryStreamからのIterableを使用するstreamを公開します。

UNIXシステム(逆コンパイルされたSun.nio.fs.UnixFileSystemProvider.class)では、イテレータをロードします:Sun.nio.fs.UnixSecureDirectoryStreamが使用されます(ディレクトリ内での反復中にファイルロックを使用)。

したがって、ここのエントリをループするイテレータがあります。

それでは、カウントメカニズムを見てみましょう。

実際のカウントは、 Java 8ストリーム によって公開されるカウント/合計を削減するAPIによって実行されます。理論的には、このAPIは(multihtreadingを使用して)それほど労力をかけることなく並列操作を実行できます。しかし、ストリームは並列処理を無効にして作成されているため、うまくいきません...

このアプローチの良い面は、メモリに配列をロードしないことですエントリは、基礎となる(ファイルシステム)APIによって読み取られるときにイテレータによってカウントされます。

最後に、情報については、概念的にはファイルシステムで、ディレクトリノードは、含まれるファイルのnumberを保持する必要はありませんが、just 子ノードのリスト(iノードのリスト)が含まれます。私はファイルシステムの専門家ではありませんが、UNIXファイルシステムはそのように機能すると信じています。そのため、この情報を直接保持する方法があるとは考えられません(つまり、どこかに子ノードのリストが常に隠れている可能性があります)。

27
superbob

残念ながら、私はそれがすでに最善の方法であると信じています(ただし、 list()Fileを構築しないため、listFiles()よりもわずかに優れています。オブジェクト)。

16
Michael Myers

実際には合計数は必要なく、実際には特定の数(あなたの場合は5000)の後にアクションを実行したいので、 Java.nio.file.Files.newDirectoryStream 。利点は、カウントを取得するためだけにディレクトリ全体を移動する代わりに、早く終了できることです。

public boolean isOverMax(){
    Path dir = Paths.get("C:/foo/bar");
    int i = 1;

    try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
        for (Path p : stream) {
            //larger than max files, exit
            if (++i > MAX_FILES) {
                return true;
            }
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    }

    return false;
}

DirectoryStreamの-​​ interface doc にも良い例があります。

6
mateuscb

本当に(> 100'000)多くのファイルを含むディレクトリがある場合、ここに(移植性のない)方法があります:

String directoryPath = "a path";

// -f flag is important, because this way ls does not sort it output,
// which is way faster
String[] params = { "/bin/sh", "-c",
    "ls -f " + directoryPath + " | wc -l" };
Process process = Runtime.getRuntime().exec(params);
BufferedReader reader = new BufferedReader(new InputStreamReader(
    process.getInputStream()));
String fileCount = reader.readLine().trim() - 2; // accounting for .. and .
reader.close();
System.out.println(fileCount);
4
Renaud

シガーを使用すると役立ちます。 Sigar 統計を取得するためのネイティブフックがあります

new Sigar().getDirStat(dir).getTotal()
2
user2162827

残念ながら、mmyersが言ったように、File.list()はJavaを使用するのと同じくらい高速です。速度があなたが言うほど重要であるなら、あなたは [〜#〜] jni [〜#〜] を使用してこの特定の操作を行うことを検討したいかもしれません。その後、特定の状況とファイルシステムに合わせてコードを調整できます。

1
Sebastian Celis
public void shouldGetTotalFilesCount() {
    Integer reduce = of(listRoots()).parallel().map(this::getFilesCount).reduce(0, ((a, b) -> a + b));
}

private int getFilesCount(File directory) {
    File[] files = directory.listFiles();
    return Objects.isNull(files) ? 1 : Stream.of(files)
            .parallel()
            .reduce(0, (Integer acc, File p) -> acc + getFilesCount(p), (a, b) -> a + b);
}
1