Java.use(better);
Java.use(better);
Episode#03
拡張可能な for 文(2)-- for と別れる50の方法
《関連記事》
■ 階層構造を持つデータ
 ̄ファイルシステムを利用して、任意のフォルダーを頂点とする情報を体系的に検索/管理できると便利です。フォルダーの中には、ファイルのほかに別のフォルダーがあり、その中にまたファイルがあるという、再帰的な階層構造をしています。
□ リスト形式で表示する
 ̄まず、指定したフォルダー(ディレクトリー)を頂点として、その直下にあるファイル/フォルダーの名前を「リスト形式」で列挙します。たとえば、
// case #4: Java - リスト形式で表示する String pathname = "java/api/org/omg/CORBA_2_3"; for (File e: new ExFile(pathname)) { System.out.println(e.getName()); }
これを実行すると、次の結果が得られます。
$ java apple.pie._Tips class-use ORB.html ORB.html package-frame.html package-summary.html package-tree.html package-use.html portable class-use Delegate.html InputStream.html ObjectImpl.html (省略) $
 ̄インターフェース Iterable に準拠するクラスは、次のように実現します。
class ExFile extends File implements Iterable{ Stack stack = new Stack (); File cur = null; public ExFile(String pathname) { super(pathname); insertAll(listFiles()); } public Iterator iterator() { return new Iterator () { public boolean hasNext() { if (stack.empty()) return false; cur = stack.pop(); if (cur.isDirectory()) { insertAll(cur.listFiles()); } return true; } public File next() { return cur; } public void remove() {} }; } private void insertAll(File[] list) { int index = stack.size(); for (File e: list) { stack.add(index, e); } } }
 ̄スタックに要素を積むときには、後で取り出しやすいように逆順にします。しかし、要素を逆順に並べるのは面倒です。そこで、メソッド insertAll では、要素を挿入する位置 index を固定したまま、要素を末尾に追加します。すると、逆順に並べたのと同じ結果になります。
 ̄拡張 for 文の対象には、インターフェース Iterable に準拠する「任意のオブジェクト」を指定できます。それには、メソッド iterator を再定義する必要があります。さらに、インターフェース Iterator に準拠するメソッド hasNext/next/remove を再定義すると、拡張 for 文を介して、オブジェクトに最適な制御構造を利用できます。
 ̄メソッド hasNext では、次の要素が存在するかを判定します。スタックから要素 cur を取り出して、それがディレクトリー isDirectory なら、その中に含まれる一連のファイル listFiles をスタックに積みます。
 ̄メソッド next では、スタックから取り出した要素 cur を返します。
□ ツリー形式で表示する
 ̄次に、同じ情報を「ツリー形式」で列挙します。たとえば、
// case #5: Java - ツリー形式で表示する String pathname = "java/api/org/omg/CORBA_2_3"; System.out.println(pathname); for (Node e: new ExFile2(pathname)) { System.out.println(e); }
これを実行すると、次の結果が得られます。
$ java apple.pie._Tips java/api/org/omg/CORBA_2_3 + class-use . ORB.html . ORB.html . package-frame.html . package-summary.html . package-tree.html . package-use.html + portable + class-use . Delegate.html . InputStream.html . ObjectImpl.html (省略) $
ここで「+」に続くのはフォルダー「.」に続くのはファイルです。また、先行するインデントが階層(深さ/高さ)に相当します。
 ̄インターフェース Iterable に準拠するクラスは、次のように実現します。
class ExFile2 extends File implements Iterable{ List seq = new Vector (); public ExFile2(String pathname) { super(pathname); walk(seq, new File(pathname), 0); } public Iterator iterator() { return seq.iterator(); } private void walk(List seq, File file, int depth) { for (File e: file.listFiles()) { if (e.isDirectory()) { seq.add(new Node(e, "+", depth)); this.walk(seq, e, depth+1); } else { seq.add(new Node(e, ".", depth)); } } } }
 ̄メソッド walk では、指定した file を基点に、親から子へと移動 walk しながら、ファイル/フォルダーが混在する階層構造(ツリー)を巡回します。各要素 Node を seq の末尾に追加するときに、末端の葉にある(子を持たない)ファイルと、その途中の節にある(子を持つ)フォルダーとでは、対処が異なります。また、親から子へと移動するときには、実引数の値を増加 depth+1 させて、現在の位置(深さ)を記録します。
 ̄ここで注目に値するのは「再帰的なデータ構造に呼応する、再帰的なメソッド呼び出し」です。同じ制御構造を何度も記述するのは面倒なだけでなく、コードが煩雑で見通しも悪くなります。この問題を解決するのが、次のメソッドです。
 ̄メソッド iterator を再定義すると、拡張 for 文を介して、再帰的なデータ構造に依存せずに、各要素を巡回できます。
↑ TOP
》作業中です《