ディレクトリ内のファイルを一覧表示する
File クラスの list() メソッドを使用すると、ディレクトリ内のすべてのファイルのファイル名を簡単に一覧表示できます。ファイル名以外のファイルを取得したい場合は、 listFiles() メソッドを使用できます。これは非常に簡単ですが、難しいのは返されたリストをどのように処理するかです。従来の長い外部反復子を使用する代わりに、エレガントな関数式を使用してこのリストを実際に走査します。ここでは、JDK の新しい CloseableStream インターフェイスと、関連するいくつかの高階関数も使用する必要があります。
次のコードは、現在のディレクトリ内のすべてのファイルの名前をリストします。
次のようにコードをコピーします。
Files.list(Paths.get("."))
.forEach(System.out::println);
他のディレクトリをリストしたい場合は、「.」をアクセスしたいディレクトリのフルパスに置き換えます。
ここでは、まず Paths の get() メソッドを使用して、文字列を通じて Path インスタンスを作成します。次に、ファイル ツール クラスの list() メソッドを通じて ClosableStream オブジェクトを取得し、それを使用してディレクトリ内のすべてのファイルを走査できます。次に、内部反復子 forEach() を使用してファイル名を出力します。まず、このコードの出力結果の一部を見てみましょう。現在のディレクトリ内のファイルとサブディレクトリを一覧表示します。
次のようにコードをコピーします。
./aSampleFiles.txt
./bin
./fpij
...
ファイルではなく、現在のディレクトリのサブディレクトリのみを取得したい場合は、filter() メソッドを使用できます。
次のようにコードをコピーします。
Files.list(Paths.get("."))
.filter(ファイル::isディレクトリ)
.forEach(System.out::println);
ilter() メソッドは、ファイル ストリームからディレクトリをフィルタリングします。ラムダ式を渡す代わりに、Files クラスの isDirectory メソッドへの参照を渡します。 filter() メソッドにはブール値を返す Predicate 型が必要であることを思い出してください。このメソッドはまさに適切です。最後に、内部反復子を使用してディレクトリの名前を出力します。プログラムは現在のディレクトリのサブディレクトリを出力します。
次のようにコードをコピーします。
./bin
./fpij
。/出力
...
Java での古い記述方法に比べて、この方法で記述するのははるかに簡単で、多くのコードを節約できます。特定のパターンに一致するファイルを一覧表示する方法を見てみましょう。
ディレクトリ内で指定されたファイルを一覧表示する
Java は長い間、ファイル名をフィルタリングするための list() メソッドのバリアントを提供してきました。このバージョンの list() メソッドは、FilenameFilter タイプのパラメータを受け入れます。このインターフェイスには、accept() メソッドが 1 つだけあり、File dir (ディレクトリを表す) と String name (ファイル名を表す) の 2 つのパラメータを受け入れます。 accept() メソッドが true を返した場合、ファイル名は返されたリストに表示されますが、それ以外の場合は表示されません。このメソッドを実装してみましょう。
通常のアプローチは、FilenameFilter インターフェイスを実装する匿名内部クラスのインスタンスを list() メソッドに渡すことです。たとえば、このメソッドを使用して fpij ディレクトリ内の .java ファイルを返す方法を見てみましょう。
次のようにコードをコピーします。
最終 String[] ファイル =
new File("fpij").list(new java.io.FilenameFilter() {
public boolean accept(最終ファイルディレクトリ、最終文字列名) {
return name.endsWith(".java");
}
});
System.out.println(ファイル);
数行のコードを記述するには、実際にはかなりの時間と労力がかかります。この種のコードはノイズが多すぎます: オブジェクトの作成、関数の呼び出し、匿名内部クラスの定義、クラスへのメソッドの埋め込みなど。もうこの苦痛に耐える必要はありません。2 つのパラメーターを受け取り、ボリアン値を返すラムダ式を渡すだけです。残りは Java コンパイラが処理します。
前の例では、単に匿名内部をラムダ式に置き換えることができましたが、さらに最適化する余地があります。新しい DirectoryStream ツールは、大規模なディレクトリ構造をより効率的に横断するのに役立ちます。この方法を試してみましょう。これは、追加のフィルターを受け入れる newDirectoryStream() メソッドのバリアントです。
次のようにコードをコピーします。
Files.newDirectoryStream(
Paths.get("fpij"), path -> path.toString().endsWith(".java"))
.forEach(System.out::println);
このようにして、匿名の内部クラスを取り除き、煩雑なコードを簡潔かつ明確にします。両方のバージョンの出力は同じです。指定したファイルを印刷してみましょう。
このコードは、指定されたディレクトリに .java ファイルのみを出力します。以下はその出力の一部です。
次のようにコードをコピーします。
fpij/比較.java
fpij/IterateString.java
fpij/ListDirs.java
...
ファイル名に基づいてファイルをフィルタリングします。ファイルが実行可能、読み取り可能、書き込み可能かどうかなどのファイル属性によっても簡単にフィルタリングできます。これを行うには、FileFilter タイプのパラメータを受け入れる listFiles() メソッドが必要です。匿名の内部クラスを作成する代わりに、ラムダ式を引き続き使用します。次に、現在のディレクトリ内のすべての隠しファイルをリストする例を見てみましょう。
次のようにコードをコピーします。
Final File[] files = new File(".").listFiles(file -> file.isHidden());
大きなディレクトリを操作している場合は、File のメソッドを直接呼び出す代わりに DirectoryStream を使用できます。
listFiles() メソッドに渡すラムダ式の署名は、FileFilter インターフェイスの accept() メソッドの署名と同じです。このラムダ式は、File インスタンスのパラメーターを受け入れます。この例では、パラメーター名は file です。ファイルに隠し属性がある場合は true を返し、そうでない場合は false を返します。
実際、ここではラムダ式を渡さないことで、コードをより簡潔にすることができます。
次のようにコードをコピーします。
new File(".").listFiles(File::isHidden);
最初にラムダ式を使用して実装し、次にメソッド参照を使用してより簡潔になるようにリファクタリングしました。新しいコードを作成する場合は、もちろんこの簡潔な方法を使用する必要があります。このような優れた実装が早期に見つかった場合は、間違いなくそれを優先して使用するでしょう。 「まず動作させてから改善する」という言葉がありますが、それを明確にした後、最適化のためにコードを実行します。
ディレクトリから指定したファイルをフィルタリングする例を使用します。指定したディレクトリの下のサブディレクトリを移動する方法を見てみましょう。