listFilesメソッド - Fileクラス - 萌えJava超入門
Fileクラス

ディレクトリ内のファイルを
再帰的に取得する

listFilesメソッドを利用してディレクトリ内のファイルを再帰的に取得します。

目次

1. 再帰メソッドとは


再帰メソッドとは、メソッドの中で自分自身と同じメソッドを呼び出すプログラムの書き方です。
「再帰呼出しする」
「再帰(的な)プログラム」
の様な言葉の使い方をします。

萌えJava超入門 萌えJava超入門
再帰って言葉、
一回出てきたことないっスか?
なんかトラウマ的な感じっス。




萌えJava超入門
よく覚えてたなぁ。
確かに参考程度だが
ちょっとだけ紹介した。
萌えJava超入門
再帰メソッドは、言語の種類を問わず
プログラミングの難所の一つだろう。
少しずつ慣れていこう。
萌えJava超入門 萌えJava超入門


2. listFilesメソッドを再帰呼出しする


listFilesメソッドは、自身であるディレクトリ直下にあるファイルとディレクトリだけが対象です。
でも、実際にはそれより深い階層のファイルやディレクトリに対してもリスト化したい場面が多いですよね。
そんな時には、「再帰呼出し」が役に立ちます。
Fileクラスは再帰呼出しして使わないと威力を十分に発揮できないメソッドが多いので、
ここで少し慣れておきましょう。

萌えJava超入門
Fileクラスは再帰呼出しして
利用することも多いんだけど、
ちょっと難しいよね。

Sample11_02 の仕様

  • 変数「strDir」に代入した抽象パス名に含まれるファイルとディレクトリを再帰的にリスト化します。
  • addListメソッドが再帰メソッドです。
  • 最大で、定数 MAX_SIZE の数までパス名を取得できます。
  • これを超えるファイルとディレクトリがあった場合には、
    例外「ArrayIndexOutOfBoundsException」がスローされます。
    ホントは ArrayListクラスを利用したいところですが、まだ習ってないので配列でがんばりましょう。 )

萌えJava超入門 萌えJava超入門
そんなに長いソースでもないのに
再帰メソッドのところが
わかりずらいっスね。
//Sample11_02.java

import java.io.File;

class Sample11_02{

    //クラス変数
    static final int MAX_SIZE = 30; // (1) とりあえず 30こ
    static File[] result = null; // (2) 結果入力用の配列
    static int i = 0; // (3) result カウンタ変数

    public static void main(String[] args){

        //初期化
        String strDir = "parent"; // (4) 検査対象の抽象パス名
        result = new File[MAX_SIZE]; // (5) 配列の初期化

        //再帰メソッドの呼び出し
        File dir = new File(strDir);
        if(dir.exists()){
            addList(dir);
        }

        //結果表示
        int j=0;
        for(; j < result.length; j++){ // ...(6)
            File file = result[j];
            if(file!=null){
                String strPath =
                    String.format("%02d %s", j, file.getPath());// ...(7)
                System.out.println(strPath);
            }else{
                break;
            }
        }
        System.out.println("Hit: " + j);

    }
    //再帰メソッド
    private static void addList(File dir){

        result[i]= dir;  // ...(a)
        i++;  // ...(b)
        if(dir.isDirectory()){  //再帰or終了 の分岐1
            File[] files = dir.listFiles();// ...(c)
            if(files!=null){  //再帰or終了 の分岐2
                for(File file : files){
                    addList(file);//再帰呼出し ...(f)
                }
            }else{
                //終了 ...(e)
            }
        }else{
            //終了 ...(d)
        }
    }
}

萌えJava超入門
addListメソッドの中に
addListメソッドの中が出てくる。
これらは全て異なるパスをひとつずつ受ける。
この引数は result に代入されて、
ディレクトリの場合だけ再帰するんだ。


クラス変数

  1. final int MAX_SIZE ...(1)
  2. 結果を保持するFileクラス配列の要素数です。
    必要な要素数が分からないので、多めに準備しています。
    要素数が可変である ArrayListクラスを使用すれば、よりスマートでしょう。
  3. File[] result ...(2)
  4. 結果を保持するFileクラスの配列です。
    resultは、static なので、クラス内に1つだけです。
  5. int i ...(3)
  6. result のカウンタ変数です。
    カウンタ変数 i は、resultへの代入が起こるたびに必ず1つ増えます。
    i も、static なので、クラス内に1つだけです。
  result は、再帰する度に実行される addListメソッドから常に参照できるように
  クラス変数にしています。
  また、result のカウンタ変数 i も同様です。

  ご参照↓
     第十三章 変数とスコープ
   クラス変数のスコープ

初期化

  1. strDir ...(4)
  2. 検査対象の抽象パス名です。
  3. result = new File[MAX_SIZE]; ...(5)
  4. 配列 result を初期化しています。

再帰メソッドの呼び出し

  strDirが実在すれば、addListメソッドを実行します。
  addListメソッドは、自身の中で addListメソッドを実行する 再帰メソッドです。

addListメソッド内の処理

  1. result[i] = dir; ...(a)
  2. dirがファイルでもディレクトリでも、resultに代入されます。
    つまり、addListメソッドの引数は、すべて resultに代入されます。
    resultへの代入は、この一か所だけです。
  3. i++ ...(b)
  4. カウンタ変数 i を1つ増やします。
    カウンタ変数 i の変更は、この一か所だけです。
  5. dirがディレクトリの場合 ...(c)
  6. listFilesメソッドが実行されます。
  7. dirがファイルの場合 ...(d)
  8. この addListメソッドは終了します。(↓Point参照)
  9. files が null(dirが空のディレクトリ)の場合 ...(e)
  10. この addListメソッドは終了します。(↓Point参照)
  11. dirが空でない場合 ...(f)
  12. filesの要素一つ一つを引数にして addListメソッドを実行します。
    filesの要素数だけ addListメソッドが実行されます。
      それぞれの addListメソッドで、(a)から実行します。

  13. 終了条件
  14. すべての addListメソッドが、ファイル、又は、空のディレクトリに行き当たれば終了します。

Point
再帰メソッドには、再帰と終了に分岐する部分が必要です。
 addListメソッドでは、
  if(dir.isDirectory()) と、
  if(files!=null) の2か所で分岐しています。
萌えJava超入門
そうでないと
無限ループになっちゃうからな。



萌えJava超入門 萌えJava超入門

結果表示

以下は、resultの表示の為のソースコードです。
  1. int j=0; for(; j < result.length; j++) ...(6)
  2. ご参照↓
     第十三章 変数とスコープ
      for文の中で宣言した変数のスコープ
       2.初期化式をfor文の外に書く

  3. String.format ...(7)
  4. ご参照↓
     第十八章 Stringクラス
      formatメソッド




実行時のファイルの配置例
    ws
    |----Sample11_02.java
    └----parent
          |----child01
          |     |----child01_1
          |     |     |----fileA.txt
          |     |     |----fileB.txt
          |     |     └----fileC.txt
          |     |
          |     |----file01_1.txt
          |     |----file01_2.txt
          |     └----file01_3.txt
          |
          |----child02
          |     |----child02_01
          |     |     |----fileX.txt
          |     |     |----fileY.txt
          |     |     └----fileZ.txt
          |     |
          |     └----file02.txt
          |
          |----child03
          |     |----file997.txt
          |     |----file998.txt
          |     └----file999.txt
          |
          |----child04
          |----file01.txt
          └----file02.txt


コマンドライン
>cd ws
ws>javac -encoding UTF-8 Sample11_02.java
ws>java Sample11_02
00 parent
01 parent/child01
02 parent/child01/child01_1
03 parent/child01/child01_1/fileA.txt
04 parent/child01/child01_1/fileB.txt
05 parent/child01/child01_1/fileC.txt
06 parent/child01/file01_1.txt
07 parent/child01/file01_2.txt
08 parent/child01/file01_3.txt
09 parent/child02
10 parent/child02/child02_01
11 parent/child02/child02_01/fileX.txt
12 parent/child02/child02_01/fileY.txt
13 parent/child02/child02_01/fileZ.txt
14 parent/child02/file02.txt
15 parent/child03
16 parent/child03/file997.txt
17 parent/child03/file998.txt
18 parent/child03/file999.txt
19 parent/child04
20 parent/file01.txt
21 parent/file02.txt

ws>

萌えJava超入門
出力結果の順番をじっくり見てみると
アルゴリズムを理解する上でヒントになるぞ。

萌えJava超入門
う~ん。
説明されれば分かった気がするんだけど、
書けと言われたらちょっとムリ。
萌えJava超入門
合わせ鏡を覗いている様で、
直感的にわからないのが
辛いっスね。



萌えJava超入門
合わせ鏡ね...。
まあいい例えかな。
今後も出てくると思いますので、少しずつ慣れればいいでしょう。



お疲れ様でした。




© 2019 awasekagami