第17回 勉強会資料(PDF 216KB)

Java8 Streamの紹介
JDK8の新機能
• Lambda式に対応
– 関数型インターフェース
– メソッド参照
言語拡張されましたー(簡単に説明します)
• Stream API
今日の主題です。Lambda式を使う便利な機能
• 日付API
Calendarの代替APIだけど微妙に使いにくい
Lambda式導入前は?
• リフレクションAPIで関数を参照
参照の取り出しや呼出の例外満載
→パフォーマンスが微妙(いまはそうでもないけど)
→動的参照なので呼出を追いにくい
• インターフェースで実装を切り替える
GoFのCommandパターンやStrategyパターン
→いちいちクラスを丁寧に書くのタルイ
Lambda導入
• .NETにあるよね
– あるよ。やっと使えるようになったんですよ。
• 書式
(Integer x, Integer y) -> { return x+y; } //基本
(x, y) -> { return x+y; } //引数省略可能
(x, y) -> x+y;
//処理が1つならreturn {}省略可
x -> ”hoge is ”+x;
//引数が1つなら左辺()も省略可
式の格納(関数型インターフェース)
• 定義方法
BinaryOperator<Integer> plus = (x, y) -> x+y;
使用例)
Integer v = plus.apply(1,2);
既存でインターフェース定義してコマンドパターン呼出する感じ
と同じ。基本的なインターフェースはJDKのAPIに含まれます.
メソッド参照
• 既存のメソッド(クラス/インスタンス)を関数型イ
ンターフェースにセットすることができます.
例1)
LongSupplier currentTime = System::currentTimeMillis;
例2)
Date currentDate = new Date();
LongSupplier currentTime2 = currentDate ::getTime;
使ったことある例)
プロパティの値の文字列が空でなかったら、備考に見出し+
文字列をセットする
こんな感じ
public void storeBiko(Supplier<String> getter, String header,
Consumer<String> bikoSetter) {
String str = getter.get();
if(str!=null && str.length()>0) {
bikoSetter.apply(header+str);
}
}
呼出方法
storeBiko(bean::getName, “名前:”, bean::setBiko):
Stream
• データ集合を処理して別の集合や計算結果
を取得します。
– JDK8からの機能
– IOのInputStreamとかとは違います
• 使い方
– コレクションなどからStreamを作成
– Streamを処理するメソッドを呼ぶ(複数かも)
– 結果のデータの取り出し
例1 リストの文字列を数字に変換
List<String> src = Arrays.asList(“0”, “1”, “2”, “3”, “3”, “-1");
// Streamの書式
List<Integer> dest = src.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
//既存の書式
List<Integer> dest = new ArrayList<Integer>();
for(String s : src) {
dest.add(Integer.parseInt(s));
}
例2 1の処理に加えて負の数は除外
// Streamの書式
List<Integer> dest = src.stream()
.map(s -> Integer.parseInt(s))
.filter(n -> n>=0)
.collect(Collectors.toList());
//既存の書式
List<Integer> dest = new ArrayList<Integer>();
for(String s : src) {
final int n = Integer.parseInt(s);
if(n>=0) {
dest.add(n);
}
}
例3 文字列と項目の存在数のカウント
// Stream
Map<String, Long> dest = src.stream()
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
// 既存の実装
Map<String, Long> dest = new HashMap<String, Long>();
for(String s : src) {
long c = 0L;
if(dest.containsKey(s)) {
c = dest.get(s);
}
c++;
dest.put(s, new Long(c));
}
ファイルから行単位でBeanを読取
// //// 行からBeanをパースするのはBean.parse(String)のメソッドで行う想定。
// Stream利用
Path path = FileSystems.getDefault().getPath(“data.txt”);
List<Bean> dest = Files.lines(path)
.flatMap(line -> Bean.parse(line)).collect(Collectors.list());
// 1.7の機能で
List<Bean> dest = new ArrayList<Bean>;
try (BufferedReader br = new BufferedReader(new FileReader(“data.txt"))) {
dest.add(Bean.parse(br.readLine()));
}
// 参考1.7よりも前
List<Bean> dest = new ArrayList<Bean>;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(“data.txt”));
dest.add(Bean.parse(br.readLine()));
} finally {
if(br!=null) {
br.close();
}
}
その他メリットまとめ
• Streamの処理で複数処理するとメニーコア時
代にいぃ感じ (Stream#parallels()を挟む)
コンパイラなど言語環境が成熟したら、よ
り高速効率的な実行ができる可能性
• ループ条件分岐のブロック記述がネストを深
くしなくても記述できる
記述の柔軟性
Optional
• 特殊な値を表す
– いままでNULLとか-1にしていたもの
• NullPointExceptionよけ
• 値があったら処理する、なかったらXXするを
厳格に定義する
例) Optinalの値が有効な場合だけ表示
Optional#ifPresent(v -> System.out.println(v));