limit / skip を一言でいうと
limit と skip は、どちらも 「ストリームから流れてくる要素の“範囲”を切り取るための中間操作」 です。
ざっくり言うと、limit(n) は「先頭から n 個だけに絞る」、skip(n) は「先頭から n 個を捨てて、その後ろを流す」ためのものです。
ページング(「何件目から何件目までを表示するか」)や、「とりあえず先頭の数件だけ見たい」といった場面でよく使われます。
limit の基本とイメージ
limit(n) は「先頭から n 個だけ通すフィルター」
limit(long maxSize) は、ストリームの先頭から最大 maxSize 個までの要素だけを次に流し、それ以降の要素は無視します。
import java.util.List;
public class LimitBasic {
public static void main(String[] args) {
List<String> list = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
list.stream()
.limit(3)
.forEach(System.out::println);
// 出力: 1 2 3
}
}
Javaここでは、「1〜10」のうち、先頭 3 つだけが forEach に届きます。
重要なのは、limit も他の中間操作と同じく「遅延評価」であり、終端操作(ここでは forEach)が呼ばれたときに初めて効き始める、という点です。
skip の基本とイメージ
skip(n) は「先頭から n 個を捨てる」
skip(long n) は、ストリームの先頭から n 個の要素を読み飛ばし、それ以降の要素だけを次に流します。
import java.util.List;
public class SkipBasic {
public static void main(String[] args) {
List<String> list = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
list.stream()
.skip(2)
.forEach(System.out::println);
// 出力: 3 4 5 6 7 8 9 10
}
}
Javaこの場合、「1」「2」は skip(2) によって捨てられ、「3」以降だけが forEach に届きます。
skip も中間操作なので、それ単体では何も起こらず、終端操作が呼ばれたときに初めて実行されます。
limit と skip を組み合わせた「ページング」設計
何件目から何件目まで、を Stream で書く
例えば、「リストの 6〜10 件目だけを取り出したい」とします(1 始まりで数えるイメージ)。
これは、先頭 5 件を skip(5) で飛ばし、その後の 5 件を limit(5) で切り取れば実現できます。
import java.util.List;
public class PagingExample {
public static void main(String[] args) {
List<Integer> list = List.of(1,2,3,4,5,6,7,8,9,10);
List<Integer> page =
list.stream()
.skip(5) // 先頭 5 件を飛ばす(6 から始まる)
.limit(5) // そこから 5 件だけ取る(6〜10)
.toList();
System.out.println(page); // [6, 7, 8, 9, 10]
}
}
Javaこの「skip((page-1)*size) → limit(size)」という形は、DB のページングや画面表示のページングを Stream で表現するときの定番パターンです。
limit / skip と「遅延評価」の関係をもう少し深掘りする
limit は「必要な分だけで止める」
limit は、終端操作が要素を消費していく中で、「指定された個数に達したら、それ以降の要素はそもそも処理しない」という動きをします。
例えば、peek でログを出しながら limit するコードを考えます。
import java.util.stream.Stream;
public class LimitPeekExample {
public static void main(String[] args) {
Stream.of(1,2,3,4,5,6,7,8,9)
.peek(x -> System.out.println("A" + x))
.limit(3)
.peek(x -> System.out.println("B" + x))
.forEach(x -> System.out.println("C" + x));
}
}
Java出力は概ねこうなります。
A1
B1
C1
A2
B2
C2
A3
B3
C3
limit(3) のおかげで、「A」「B」「C」の処理は 1〜3 までしか実行されません。
skip は「指定数に達するまで捨て続ける」
一方、skip は「先頭から n 個を捨てる」ために、n 個分は必ず読み進めます。
Stream.of(1,2,3,4,5,6,7,8,9)
.peek(x -> System.out.println("A" + x))
.skip(6)
.peek(x -> System.out.println("B" + x))
.forEach(x -> System.out.println("C" + x));
Javaこの場合、「A1〜A9」はすべて出力されますが、「B」「C」が出るのは 7,8,9 だけです。
つまり、
先頭から「何件かだけ見たい」 → limit が効率的
先頭から「何件かを捨てて、その後ろを見たい」 → skip が必要
という役割分担になっています。
パイプラインの中での置き場所と設計感覚
filter / map / sorted と組み合わせる
limit / skip は、他の中間操作と自由に組み合わせられます。
例えば、「年齢順に並べた上で、上位 10 人だけを取りたい」なら:
List<User> top10 =
users.stream()
.sorted(Comparator.comparingInt(User::getAge).reversed()) // 年齢降順
.limit(10) // 先頭 10 人
.toList();
Javaまた、「20 歳以上のユーザーのうち、3 ページ目(1 ページ 10 件)を取りたい」なら:
int page = 3;
int size = 10;
List<User> page3 =
users.stream()
.filter(u -> u.getAge() >= 20)
.sorted(Comparator.comparing(User::getName))
.skip((page - 1L) * size)
.limit(size)
.toList();
Javaこのように、
絞り込み → 並べ替え → 範囲を切り取る
という順番で並べると、「何をしているか」が左から右に自然に読めます。
まとめ:limit / skip を自分の言葉で整理する
limit / skip をあなたの言葉でまとめるなら、
「limit は“先頭から n 個だけ通す”、skip は“先頭から n 個を捨てる”ための中間操作であり、ページングや部分取得を Stream の中で表現するための道具」 です。
特に意識しておきたいのは、
どちらも中間操作であり、終端操作が呼ばれるまで実行されないことlimit は「必要な分だけで処理を打ち切る」ので効率が良いことskip は「指定数に達するまで読み飛ばす」ので、先頭側の要素は必ず一度は通ることfilter / sorted と組み合わせることで、「条件付きソート+ページング」をきれいに書けること
あたりです。
