distinct / limit / skip — 重複排除・ページング処理
Stream API の中間操作でよく使うのが distinct(重複排除)、limit(先頭から指定件数だけ残す)、skip(先頭から指定件数を飛ばす)。これらを組み合わせると「ユニーク化」「ページング」「部分抽出」が簡単に書けます。
基本の役割
- distinct:
- 要素の重複を排除(equals/hashCode に基づく)。
- 全体を見て処理するため、大量データではメモリ負荷に注意。
- limit(n):
- 先頭から n 件だけ残す。
- ページングや「サンプル抽出」に便利。
- skip(n):
- 先頭から n 件を飛ばす。
- ページングの「開始位置」を指定するのに使う。
基本コード例
distinct(重複排除)
List<String> names = List.of("A","B","A","C","B");
List<String> uniq = names.stream()
.distinct()
.toList();
System.out.println(uniq); // [A, B, C]
Javalimit(先頭から指定件数)
List<Integer> nums = List.of(1,2,3,4,5);
List<Integer> first3 = nums.stream()
.limit(3)
.toList();
System.out.println(first3); // [1, 2, 3]
Javaskip(先頭を飛ばす)
List<Integer> nums = List.of(1,2,3,4,5);
List<Integer> after2 = nums.stream()
.skip(2)
.toList();
System.out.println(after2); // [3, 4, 5]
Java例題で理解する
例題1: ユーザーリストから「重複を除いた先頭10件」
List<String> users = List.of("u1","u2","u1","u3","u4","u2","u5","u6","u7","u8","u9","u10","u11");
List<String> page = users.stream()
.distinct() // 重複排除
.limit(10) // 先頭10件
.toList();
System.out.println(page); // [u1, u2, u3, u4, u5, u6, u7, u8, u9, u10]
Java例題2: ページング処理(2ページ目を取得)
List<Integer> data = IntStream.rangeClosed(1, 50).boxed().toList();
int pageSize = 10;
int pageNum = 2; // 0始まり → 2ページ目は skip(20)
List<Integer> page = data.stream()
.skip(pageNum * pageSize)
.limit(pageSize)
.toList();
System.out.println(page); // [21,22,23,24,25,26,27,28,29,30]
Java- ポイント: skip で開始位置を飛ばし、limit で件数を制御。
例題3: ログから「重複行を除き、最新20件だけ」
List<String> logs = List.of("WARN a","ERROR b","WARN a","INFO c","ERROR b","DEBUG d");
List<String> latest = logs.stream()
.distinct()
.skip(Math.max(0, logs.size() - 20)) // 最新20件に絞る
.toList();
System.out.println(latest); // [WARN a, ERROR b, INFO c, DEBUG d]
Javaテンプレート集
- 重複排除
stream.distinct().toList();
Java- 先頭N件だけ
stream.limit(N).toList();
Java- 先頭M件を飛ばす
stream.skip(M).toList();
Java- ページング(pageNum, pageSize)
stream.skip(pageNum * pageSize)
.limit(pageSize)
.toList();
Java- 重複排除+ページング
stream.distinct()
.skip(pageNum * pageSize)
.limit(pageSize)
.toList();
Java落とし穴と回避策
- distinct は equals/hashCode に依存:
- 独自クラスでは equals/hashCode を正しく実装しないと期待通りに動かない。
- skip + limit の順序:
- ページングは必ず skip → limit の順。逆だと結果が変わる。
- 大量データでの distinct:
- 全件を保持して判定するためメモリ負荷が大きい。必要なら DB 側で distinct する、またはチャンク処理を検討。
- 無限ストリームで limit を忘れる:
Stream.generateやiterateは必ず limit/takeWhile を入れて有限化。
まとめ
- distinct: 重複排除(equals/hashCode 基準)。
- limit: 先頭から指定件数だけ残す。
- skip: 先頭から指定件数を飛ばす。
- 組み合わせ: ページングや部分抽出に最適。
👉 練習課題: 「商品リストから重複を除き、3ページ目(1ページ10件)の商品名を取得」してみると、distinct・skip・limit の流れが自然に理解できます。
