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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

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

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

Avatar for Yuichi.Sakuraba

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などを使ってイミュータブルにする