Upgrade to Pro — share decks privately, control downloads, hide ads and more …

あつめたデータをどう扱うか

 あつめたデータをどう扱うか

2022.01.30 JJUG ナイトセミナー「コレクションフレームワーク特集」資料

Yuichi.Sakuraba

January 30, 2023
Tweet

More Decks by Yuichi.Sakuraba

Other Decks in Technology

Transcript

  1. 櫻庭 祐一 • Java歴 27年 • Javaに関する著作多数 • WEB+DB Press連載

    「見なおそう! モダンJavaの流儀」 • 書籍「Java SE 7/8 速攻入門」 et al. • 日本で1人目のJava Champion • JJUG創設メンバー
  2. データの利用例 平均を求める • リストに4人の成績がまとめられているので、 平均点を求める • 4人だからいいけど、人数が増えたら? • インデックス書き間違えない? •

    人数が増えたとき人数を更新しわすれない? List<Integer> scores = ...; int sum = 0; sum += scores.get(0); sum += scores.get(1); sum += scores.get(2); sum += scores.get(3); double ave = (double)sum/4;
  3. List<Integer> scores = ...; int sum = 0; sum +=

    scores.get(0); sum += scores.get(1); sum += scores.get(2); sum += scores.get(3); double ave = (double)sum/4; データの利用例 平均を求める • リストに4人の成績がまとめられているので、 平均点を求める インデックスが異なるだけで 同じ処理を繰り返している 処理をくくりだして ループでまとめる
  4. コレクションとループ Collection 処理 Collection 処理 Collection 処理 処理 値 Collection

    Collection Ex 画面表示 ファイル書き込み Ex 統計処理 検索 Ex 変換/マッピング テーブル操作 Ex ファイル読み込み
  5. 例 移動平均を求める • 1時間ごとの温度データを保持したコレクションから 直近n時間の移動平均を求める List<Double> calcMovingAveTemps(List<Double> temps, int n)

    { List<Double> movingAveTemps = new ArrayList<>(); for (int i = n-1; i < temps.size(); i++) { var movingSum = 0.0; for (int j = 0; j < n; j++) { movingSum += temps.get(i - j); } movingAveTemps.add(movingSum/n); } return movingAveTemps; }
  6. for文の特徴 • ループの制御を自分で行う必要がある • インデックスを自由に扱うことができる • 行列、テンソルなどのデータ構造も扱いやすい • for文は文であるため、ループの処理結果を直接返せない •

    結果を保持させる変数をループの外側で定義する必要がある とはいうものの、インデックスを直接扱う場合は少ない インデックスを不使用なら、ループ制御はやりたくない
  7. 平均を求める – for-each文バージョン scoresの要素がscoreに代入される List<Integer> scores = ...; int sum

    = 0; for (var score: scrores) { sum += score; } double ave = (double)sum/scores.size();
  8. List<Integer> scores = ...; int sum = 0; for (var

    score: scrores) { sum += score; } double ave = (double)sum/scores.size(); 平均を求める – for-each文バージョン 変数sumが書き換え可能 ループで使用するためfinalにはできない 変数の再代入を防ぐには…
  9. final int sum = scores.stream() .collect(Collectors.summingInt(x -> x)); final double

    ave = (double)sum/scores.size(); 平均を求める – Stream APIバージョン final int sum = scores.stream() .mapToInt(x -> x) .sum(); final double ave = (double)sum/scores.size(); final double ave = scores.stream() .collect(Collectors.averagingDouble(x -> x)); Ex 1. Ex 2. Ex 3.
  10. record Price(ProductID id, int amount) {} Price maxPrice = prices.get(0);

    for (int i = 1; i < prices.size(); i++) { var price = prices.get(i); if (price.amount() > maxPrice.amount()) { maxPrice = price; } } 例 最大を見つける – for文バージョン pricesに要素がない場合 ArrayIndexOutOfBoundsException発生
  11. 例 最大を見つける – Stream APIバージョン pricesに要素がない場合のために Optionalクラスが使用される 比較を行うComparatorインタフェースを ラムダ式で記述 record

    Price(ProductID id, int amount) {} final Optional<Price> maxPrice = prices.stream() .collect(Collectors.maxBy( (p1, p2) -> p1.amount() - p2.amount()));
  12. • 顧客情報を保持するコレクションから東京の顧客を抽出 record Customer(CustomerID id, String name, String prefecture) {}

    List<Customer> tokyoCustomers = new ArrayList<>(); for (int i = 0; i < customers.size(); i++) { var customer = customers.get(i); if (customer.prefecture().equals("Tokyo")) { tokyoCustomers.add(customer); } } 例 条件に合致した要素を抽出 – for文バージョン
  13. record Customer(CustomerID id, String name, String prefecture) {} List<Customer> tokyoCustomers

    = customers.stream() .filter(c -> c.prefecture().equals("Tokyo")) .toList(); 例 条件に合致した要素を抽出 – Stream APIバージョン for文バージョンの If文と同じ条件式
  14. コレクションに対するループのまとめ for文 for-each文 Stream API ループ制御 必要 不要 不要 インデックス

    〇 × × ループ外の変数アクセス 〇 〇 △ final変数 orフィールド 値を返す × × 〇
  15. Queue • First In, First Out • 最初に入れた要素が、最初に取りだされる • 先入れ、先出し

    • データを作る側と、使う側を結びつける • Publisher-Subscriberパターン • イベント駆動アーキテクチャ D C B A
  16. Queueの使用例 - GUI マウスクリック キー入力 再描画 GUIで発生したイベントを キューに入れる MouseEventHandler KeyEventHandler

    RepaintManager イベントに登録された ハンドラーを実行 • マルチスレッドでのデータ間のやり取り • Blocking Queue
  17. Deque • First In, Last Out • 最初に入れた要素が、最後に取りだされる • 先入れ、後出し

    • スタック A B C • 状態の保存 • 逆ポーランド記法演算 • Undo, Redo
  18. Dequeの使用例 - Undo, Redo • 操作をコマンドとして表現し、スタックで管理 • Undo – スタックの最後のコマンドをキャンセル

    • Undoの回数制限がある場合、逆端からコマンドを削除 File Open 画像拡大 色調整 例 レタッチ処理
  19. Dequeの使用例 - Undo, Redo • 操作をコマンドとして表現し、スタックで管理 • Undo – スタックの最後のコマンドをキャンセル

    • Undoの回数制限に達したら、逆端からコマンドを削除 File Open 画像拡大 色調整 例 レタッチ処理 Undoのためコマンドをキャンセル
  20. Thread #1 Thread #2 If文 put() If文 put() nullなので put実行

    この時点でもnullなので put実行しvalueを上書きしてしまう これを防ぐためには Ifブロックの前にmapをロックし 単一スレッドからのアクセスに制限する 同期化
  21. 改訂版 Put If Absent処理 ReentrantLock lock = new ReentrantLock(); lock.lock();

    try { if (map.get(key) == null) { map.put(key, value); } } finally { lock.unlock(); } 注: Java 8でMapにputIfAbsentメソッドが追加されたので 本来はそちらを使うべき
  22. 並列コレクション • インタフェース • BlockingQueu/BlockingDeque • ConcurrentMap • ConcurrentNavigableMap •

    主なクラス • ConcurrentHashMap • ConcurrentLinkedQueue/Deque • CopyOnWriteArrayList/Set
  23. 並列コレクションと同時アクセス数のめやす 同時アクセス数 生成後はReadのみ ほぼRead ReadもWriteも 2 ~ 3 並列コレクション Immutable

    Collection 並列コレクション 並列コレクション ~ 10程度 並列コレクション Immutable Collection 並列コレクション BlockingQueu/Deque 10 ~ Immutable Collection 使わない 使わない Immutable Collection: 生成後の要素の追加/変更/削除を行わないコレクション Listであれば Collections. unmodifiableList(List) 並列/並行処理ではコレクションに保持させる要素も できるかぎりRecordなどを使ってイミュータブルにする