flatMap を使用してサブディレクトリを一覧表示する
指定したディレクトリ内のファイルを一覧表示する方法を前に見てきました。指定されたディレクトリ (深さは 1) の直接のサブディレクトリをトラバースする方法を見てみましょう。最初に単純なバージョンを実装し、次に、より便利な flatMap() メソッドを使用してそれを実装します。
まず、従来の for ループを使用して、指定されたディレクトリを走査します。サブディレクトリにファイルがある場合は、それらをリストに追加します。それ以外の場合は、サブディレクトリをリストに追加します。最後に、すべてのファイルの合計数を出力します。コードは以下のとおりです。これはハードモード用です。
次のようにコードをコピーします。
public static void listTheHardWay() {
List<File> ファイル = new ArrayList<>();
File[] filesInCurrentDir = new File(".").listFiles();
for(ファイル file : filesInCurrentDir) {
File[] filesInSubDir = file.listFiles();
if(filesInSubDir != null) {
files.addAll(Arrays.asList(filesInSubDir));
} それ以外 {
ファイル.追加(ファイル);
}
}
System.out.println("カウント: " + files.size())
}
まず現在のディレクトリ内のファイルリストを取得し、それを走査します。各ファイルにサブファイルがある場合は、それらをリストに追加します。これには問題はありませんが、いくつかの一般的な問題があります: 可変性、基本的なパラノイア、命令性、コードの冗長性など。 flatMap() と呼ばれる小さなメソッドでこれらの問題を解決できます。
名前が示すように、このメソッドはマッピング後に平坦化します。これは、map() と同様に、コレクション内の要素をマップします。ただし、map() メソッドとは異なり、map() メソッドのラムダ式は要素のみを返し、ここで返されるのは Stream オブジェクトです。したがって、このメソッドは複数のストリームを平坦化し、内部の各要素を平坦化されたストリームにマップします。
flatMap() を使用してさまざまな操作を実行できますが、当面の問題がその価値を示しています。各サブディレクトリにはファイルのリストまたはストリームがあり、現在のディレクトリの下にあるすべてのサブディレクトリ内のファイルのリストを取得したいと考えています。
一部のディレクトリは空であるか、子要素が存在しない場合があります。この場合、空のディレクトリまたはファイルをストリーム オブジェクトにラップします。ファイルを無視したい場合は、JDK の flatMap() メソッドでも空のファイルを適切に処理でき、null 参照を空のコレクションとしてストリームにマージします。 flatMap() メソッドの使用法を見てみましょう。
次のようにコードをコピーします。
public static void betterWay() {
List<File> ファイル =
Stream.of(新しいファイル(".").listFiles())
. flatMap(file -> file.listFiles() == null ?
Stream.of(file) : Stream.of(file.listFiles()))
.collect(toList());
System.out.println("カウント: " + files.size());
}
まず現在のディレクトリのサブファイル ストリームを取得し、次にその flatMap() メソッドを呼び出します。次に、ラムダ式をこのメソッドに渡します。これにより、指定されたファイルのサブファイルのストリームが返されます。 flatMap() メソッドは、現在のディレクトリのすべてのサブディレクトリにあるファイルのコレクションを返します。 Collector のcollect() メソッドと toList()( メソッドを使用して、それらをリストに収集します。
flatMap() に渡すラムダ式は、ファイルのサブファイルを返します。 そうでない場合は、ファイルのストリームが返されます。 flatMap() メソッドは、このストリームをストリームのコレクションにエレガントにマップし、次にコレクションをフラット化し、最後に単一のストリームにマージします。
flatMap() メソッドは、多くの開発作業を軽減します。これは、タプルと呼ばれることが多い 2 つの連続する操作を 1 つのエレガントな操作に結合します。
flatMap() メソッドを使用して、直接のサブディレクトリ内のすべてのファイルを一覧表示する方法はすでに知っています。ファイルの変更操作を監視してみましょう。
ファイルの変更を監視する
ファイルやディレクトリの検索方法はすでに知っていますが、ファイルが作成、変更、または削除されたときにプロンプト メッセージを受け取りたい場合も、これは非常に簡単です。このようなメカニズムは、構成ファイルやシステム リソースなどの特殊なファイルの変更を監視するのに非常に役立ちます。 Java 7 で導入されたこのツール、WatchService を調べてみましょう。このツールはファイルの変更を監視するために使用できます。以下に示す機能の多くは JDK 7 からのものですが、ここでの最大の改善点は内部イテレーターによってもたらされる利便性です。
まず、現在のディレクトリ内のファイルの変更を監視する例を書いてみましょう。 JDK の Path クラスは、オブザーバー サービスのファクトリーであるファイル システムのインスタンスに対応します。次のように、このサービスの通知イベントを登録できます。
次のようにコードをコピーします。
inal パス path = Paths.get(".");
最終的な WatchService watchService =
path.getFileSystem()
.newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("1 分以内に変更されたファイルを報告します...");
現在のディレクトリへの変更を監視するために WatchService を登録しました。この WatchService をポーリングして、ディレクトリ内のファイルの変更操作を取得できます。この WatchService は、WatchKey を通じてこれらの変更を返します。キーを取得したら、そのすべてのイベントをループしてファイル更新の詳細を取得できます。複数のファイルが同時に変更される可能性があるため、ポーリング操作によって複数のイベントが返される場合があります。ポーリングとトラバーサルのコードを見てみましょう。
次のようにコードをコピーします。
Final WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);
if(watchKey != null) {
watchKey.pollEvents()
。ストリーム()
.forEach(イベント ->
System.out.println(event.context()));
}
ここでわかるように、Java 7 と Java 8 の機能が同時に表示されます。 pollEvents() によって返されたコレクションを Java 8 Stream に変換し、その内部反復子を使用して各ファイルの詳細な更新情報を出力します。
このコードを実行し、現在のディレクトリにあるsample.txtファイルを変更して、プログラムがこの更新を検出できるかどうかを確認してみましょう。
次のようにコードをコピーします。
次の 1 分以内に変更されたファイルを報告します...
サンプル.txt
このファイルを変更すると、プログラムはファイルが変更されたことを示すプロンプトを表示します。この機能を使用すると、さまざまなファイルの更新を監視し、対応するタスクを実行できます。もちろん、ファイルの作成や削除の操作のみを登録することもできます。
要約する
ラムダ式とメソッド参照を使用すると、文字列やファイルの操作、カスタム コンパレータの作成などの一般的なタスクがより簡単かつ簡潔になります。匿名の内部クラスはエレガントになり、変動性は日の出後の朝霧のように消えます。この新しいスタイルでコーディングするもう 1 つの利点は、JDK の新しい機能を使用して大きなディレクトリを効率的に走査できることです。
これで、ラムダ式を作成してメソッドに渡す方法がわかりました。次の章では、関数インターフェイスとラムダ式を使用してソフトウェアを設計する方法を紹介します。