Java8ではラムダ式やインターフェースのデフォルト値等かなり仕様が変更/追加されました。今回はその中の一つである Stream とやらを使ったプロパティファイルリーダーを組んでみたいと思います。
#まずStreamってなんぞや
Streamというのは、従来配列やList
等で表現されていた複数のオブジェクトの集合体に対する操作を分かりやすく記述出来るようにするツールと言えます。
#簡単な使い方
Stream オブジェクトを取得するにはstream()
メソッドを用います。これはJava8であればあれば便利と思われる場所には付いています。
Stream<String> s = Stream.of("a", "b", "c");
s.forEach(x -> {
System.out.println(x);
});
これは文字列のリストからStreamを生成し、順に表示させた例です。中にある見慣れないx -> {
という構文は ラムダ式 と呼ばれるJava8でStreamと同じく追加された新機能です。
ここでforEach
というメソッドが登場しました。これは名前の通り引数に指定された処理を順に適用していくメソッドで、従来の拡張forに相当します。
このように、Streamを利用することで流れ作業のようにオブジェクトを処理することが出来るようになり、コードが見やすくなります。
#実際のコード
実際にStreamを利用したプロパティファイルリーダーのコードを見てみましょう。次のようになります。
package xyz.n6g;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.stream.Collectors;
public class PropertyReader {
private File file;
private Map<String, String> props = null;
public PropertyReader(String par1FileName) {
this(new File(par1FileName));
}
public PropertyReader(File par1File) {
this.file = par1File;
load();
}
public String get(String par1Key) {
return props.get(par1Key);
}
public void load() {
try(BufferedReader br = new BufferedReader(new FileReader(this.file))) {
props = br.lines().filter(x -> {
return x.matches("(\\w+)\\s*=\\s*(.+)$");
}).collect(
Collectors.toMap(x -> {
return ((String)x).split("=")[0].trim();
}, x -> {
return ((String)x).split("=")[1].trim();
})
);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
public void show() {
System.out.println(props);
}
}
流れを追うこと自体は難しく無いと思います。load
メソッドについて見て行きましょう。
まず冒頭のtry-with-resource
でBufferedReader
を生成しています。
次に、br.lines()
というメソッドからの一連の流れが見えます。これはBufferedReader
からStreamを取得し、そのStreamに対して処理を施して最終的にMap
に落としこんでいます。
まずbr.lines()
で行ごとに切り分けられたStreamを取得し、次にfilter
というメソッドにラムダ式を渡しています。これはメソッド名の通り「引数に与えられた判定ロジックの結果から値を取捨選択するメソッド」です。ここでは正規表現でaaa = bbb
のような形式の文字列のみを読むように指定しています。
次に、collect
というメソッドを呼び出しています。これは「Streamで流れてきたオブジェクトをそれぞれ引数に与えられた"コレクター"に渡し、特定の形式に落としこむメソッド」です。ここでは、標準で用意されているCollectors.toMap
を利用しています。ここで更に指定しているラムダ式は2つあり、それぞれマップのキーと値に相当しています。つまりこの場合だと「=で切り分けた0番目をキーに、1番目を値にし、それぞれの前後に存在する空白を削除する」という処理をしています。
ここで切り分けられてMap
に落とし込まれたものが最終的な返り値となり、props
フィールドに格納されます。
#終わりに
Streamは今までの繰り返し処理の大半を綺麗に書きなおすことが出来ると思います。これを機会に学習してみて下さい。
また、Qiita初投稿だったので体裁なども見苦しい点があるとは思いますが見逃してやって下さい(