Java | Java 詳細・モダン文法:Stream API 深掘り – limit / skip

Java Java
スポンサーリンク

limit / skip を一言でいうと

limitskip は、どちらも 「ストリームから流れてくる要素の“範囲”を切り取るための中間操作」 です。
ざっくり言うと、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 と組み合わせることで、「条件付きソート+ページング」をきれいに書けること

あたりです。

タイトルとURLをコピーしました