「javaでファイルのリストを取得したら遅いよ」問題への対策

javaでファイルのリストを取得したら遅いよ。
ローカルでも遅いんだけど、ネットワーク越しだともうね。。

あまりに遅いので対策を考えてみました。

例えば指定されたディレクトリ以下のファイル名を全て取得するとこんなコードになったりしますよね。


private listFiles(File dir){
    File[] files = dir.listFiles();  // (a)
    for (File f : files) {
        if (f.isFile()){  // (c)
            String filename = f.getName();  // (b)
            System.out.println(filename);
        } else {
            listFile(f);
        }
    }
}

単純ですね。
ただ、これだと遅いんです。

Fileのインスタンスから取得している情報は下記の3つなのですが、どうやらそれぞれについて毎回システムコールを行っているようなのです。
それがネットワーク越しだと、ネットワークアクセスが発生します(たぶん)
・ディレクトリに含まれるディレクトリ/ファイルのFileインスタンス (a)
・ファイルの名前 (b)
・Fileインスタンスがディレクトリであるか (c)

で、代替手段として次のようにしました。
1. File.listFiles() の代わりにFile.list() を使う
listFilesはFileインスタンスを返しますが、listはファイル名を返します。
ファイル名からFileインスタンスを作成することで 上記の(a)と(b)が同時に取得できます。

2. isFile()の代わりに File.list() の戻り値がnullであるか判定する
対象のFileインスタンスがファイルの時、list()はnullを返すようです。
ですので、上記1でlist()を実行した際の戻り値を判定することで isFile を呼ぶ必要がなくなります。

3. (これは代替とはちょっと違いますが) Fileのメソッドの戻り値をキャッシュする
例えば File.getName() を複数回呼んでいる場合、変数に保存しておいてFileのメソッドは1回しか呼ばない

上記の処理を抜粋すると


// ファイル名のリストを取得する  
File f = new File(省略);  
String[] filenames = f.list();  
  
// ファイルであるか判定  
boolean isFile = (filenames == null);  
  
Fileのインスタンスを取得する  
File[] subfiles = Arrays.stream(filenames).map(name -> new File(f,name)).toArray(File[]::new);  

こんな感じです。

環境や処理内容にもよると思いますが、これらの対策で処理時間が1/3になってくれましたよ。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です