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));
© Copyright 2024 ExpyDoc