Kotlinのchunkedのように、Javaでサイズ/要素数によるList分割をするにはGuavaかCommons Collectionsを使えばいい

Kotlinの Iterable#chunked が便利だな~と思っていた矢先、JavaでListを複数のリストに分割する機会があった。

検索すると独自の実装方法が上位に出てくるが、たぶんCommons CollectionsGuavaに用意されてるだろうと思ったら、両方にあったのでメモ。

Kotlinのchunked

Iterable または Sequence の関数。引数として数値を取り、その要素数ごとに分割した List を返す。

val list = (0..5).toList() // 0~5までの6要素

// 引数で指定した要素数のサブリストのリストが返る
assert(list.chunked(3) == listOf(listOf(0, 1, 2), listOf(3, 4, 5)))

// 割り切れない場合、末尾のリストの要素数が少なくなる
assert(list.chunked(4) == listOf(listOf(0, 1, 2, 3), listOf(4, 5)))

// 要素数以上の値を指定した場合、先頭のリストにすべての要素が含まれる
assert(list.chunked(6) == listOf(listOf(0, 1, 2, 3, 4, 5)))
assert(list.chunked(7) == listOf(listOf(0, 1, 2, 3, 4, 5)))

第2引数で変換関数を取ることもできるが割愛。

内部的には、Iterable#windowedSequence#windowedの引数 sizestep に第1引数を、 partialWindowstrue を指定している模様。

val list = (0..5).toList()

// windowedのsizeは分割する要素数
// stepは次のリスト生成時の先頭要素の移動数
// partialWindowsはsize未満のリストを保持するか
assert(list.chunked(3) == list.windowed(size = 3, step = 3, partialWindows = true))
assert(list.chunked(5) == list.windowed(size = 5, step = 5, partialWindows = true))
assert(list.chunked(6) == list.windowed(size = 6, step = 6, partialWindows = true))
assert(list.chunked(7) == list.windowed(size = 7, step = 7, partialWindows = true))

Javaでの実装

これをJavaでやりたい場合、Guava v19.0以降、またはCommons Collections v4以降を使用する。

Guava

Guava v19.0で追加された、Lists#partitionで、第1引数のListを第2引数の要素数で分割できる。

分割された内部リストはList#subListによるもののため、第1引数で渡したリストへの変更や、内部リストへの変更がそれぞれに反映されることに注意。

Commons Collections4

Commons Collections v4.0で追加された、ListUtils#partitionを使用可能。

Adapted from http://code.google.com/p/guava-libraries/

とあるように、Guavaの Lists#partition と同じように使える。 List#subList を使っていることなども同じ。

振り返り

Commons Collectionsはv4以上でないと使えないのと、Spring Bootなどのフレームワークを使って実装している場合、依存関係で自然とGuavaが使えたりするので、Guavaのほうが利用のためのハードルは低そう。

独自で実装とかすると、エラー出たときとか面倒なので、極力ライブラリを使いたいものです。