Java標準ライブラリのみでListを分割する方法

エキサイト株式会社エンジニア佐々木です。JavaでList分割する方法をJava標準のみで実装するTipsです。

環境

$ java --version
openjdk 17.0.1 2021-10-19
OpenJDK Runtime Environment (build 17.0.1+12-39)
OpenJDK 64-Bit Server VM (build 17.0.1+12-39, mixed mode, sharing)

数字のリスト分割する

下記のリストを5件ずつ分割する

List<Integer> originalList = IntStream.rangeClosed(1, 20).boxed().toList();

// originalListの中身
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

List

これは中にあるデータでグルーピングをすると分割できます。

int partitionSize = 5;
Collection<List<Integer>>list = originalList
          .stream()
          .collect(Collectors.groupingBy(e -> (e - 1) / partitionSize))
          .values();

log.info("{}", collect);

// listの中身
// [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]

文字列のリストを分割する

下記のa-zまでの文字列をリストを5つずつ分割してみます。 下記のリストを用意します。

List<String> originalList = IntStream.rangeClosed('a', 'z').mapToObj(e -> String.valueOf((char)e)).toList();

// originalListの中身
// [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]

List.subListを使う方法

List.subListを使う方法は下記になります。

int partitionSize = 5;
List<List<Integer>> list = IntStream
        .range(0, (originalList.size()-1) / partitionSize)  // いくつリストを作るかを設定
        .mapToObj(e ->
                originalList.subList(
                        e * partitionSize, Math.min((e + 1) * partitionSize, originalList.size())  // fromIndexとtoIndexを計算してセットする
                )
        ).toList();

// listの中身
// [[a, b, c, d, e], [f, g, h, i, j], [k, l, m, n, o], [p, q, r, s, t], [u, v, w, x, y], [z]]

subListの場合は、fromIndexとtoIndexの作る必要があるので、その計算を行わないといけません。下記の部分になります。

e * partitionSize, Math.min((e + 1) * partitionSize, originalList.size())

Collectors.groupingByを使う方法

Collectors.groupingByを使うと、先程の計算処理が不要になります。 こちらだと、index / partitionSizeの値でグルーピングしつつ、Collectors.mapping(index -> originalList.get(index), Collectors.toList() でoriginalListから値を取り出し、Listに設定するということをやっています。

Collection<List<String>> list = IntStream
        .range(0, originalList.size())
        .boxed()
        .collect(
                Collectors.groupingBy(
                                index -> index / partitionSize,   // `index / partitionSize`の値でMapのKeyを作成する
                                Collectors.mapping(index -> originalList.get(index), Collectors.toList()  // MapのValueをoriginalListから取り出し、リスト生成をしてセットする
                                )
                        )
        ).values(); // 最後にコレクションに変換する

// listの中身
// [[a, b, c, d, e], [f, g, h, i, j], [k, l, m, n, o], [p, q, r, s, t], [u, v, w, x, y], [z]]

JavaのLambda式とグルーピングの処理は初見では分かりづらいかもですが、わかってしまうとかなり使いこなせそうです。

まとめ

GuavaやApacheCommonsCollectionなどのライブラリでもっと簡単にList分割をすることはできますが、このためだけに依存ライブラリを増やしたくはありません。ぜひ使ってみてください。

最後に

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

カジュアル面談はこちらになります! meety.net

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com