Kotlin 1.2で追加されたコレクションメソッド

2017年11月末、Kotlin 1.2がリリースされました。

* [Kotlin 1.2 Released: Sharing Code between Platforms](https://blog.jetbrains.com/kotlin/2017/11/kotlin-1-2-released/)
* [Kotlin 1.2 リリース: プラットフォーム間のコード共有](https://blog.jetbrains.com/jp/2017/11/29/828)
* [What's New in Kotlin 1.2](https://kotlinlang.org/docs/reference/whatsnew12.html)

Kotlin 1.2でも、Standard Libaryに[いくつかの追加](https://kotlinlang.org/docs/reference/whatsnew12.html#standard-library)があります。

この投稿では、IterableやSequence、MutableCollectionなどコレクション関連のクラスの追加関数を紹介します。

# zipWithNext : インデックスの隣り合う要素を合成したList・Sequenceを生成する

`Iterable<T>`と`Sequence<T>`の拡張関数として、zipWithNextという「インデックスの隣り合う要素を合成したリストを生成する」関数が追加されました。

`Iterable<T>`

* [Iterable<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html)
* [Iterable<T>#zipWithNext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/zip-with-next.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/generated/_Collections.kt#L2129)

`Iterable<T>#zipWithNext`の返り値型は、`List<Pair<T, T>>`です。


`Sequence<T>`

* [Sequence<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html)
* [Sequence<T>#zipWithNext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/zip-with-next.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/generated/_Sequences.kt#L1599)

`Sequence<T>#zipWithNext`の返り値型は、`Sequence<Pair<T, T>>`です。

```kotlin
val list : List<Int> = (0..5).toList()
assert(list == listOf(0, 1, 2, 3, 4, 5))


val result : List<Pair<Int, Int>> = list.zipWithNext()
assert(result == listOf(0 to 1, 1 to 2, 2 to 3, 3 to 4, 4 to 5))
```

引数に関数オブジェクトを渡し、どのように合成するかを指定することもできます。

```kotlin
val list : List<Int> = (0..5).toList()
assert(list == listOf(0, 1, 2, 3, 4, 5))

val result : List<String> = list.zipWithNext { a, b -> (a * b).toString() }
assert(result == listOf("0", "2", "6", "12", "20"))
```

SequenceのzipWithNextは、experimentalな`kotlin.coroutines.experimental`の

* buildSequence 
* yield

利用例としてとても分かりやすので、ぜひ一度[ソースコード](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/generated/_Sequences.kt#L1599)を見てみることをおすすめします。

```kotlin
val sequence : Sequence<Int> = generateSequence(0) { it + 1 }

val result : List<Pair<Int, Int>> = sequence.zipWithNext().take(3).toList()
assert(result == listOf(0 to 1, 1 to 2, 2 to 3))
```


# chunked : まとめる数を指定し、要素をまとめたList・Sequenceを生成する

`Iterable<T>`と`Sequence<T>`の拡張関数として、chunkedという「まとめる数を指定し、要素をまとめたList・Sequenceを生成する」関数が追加されました。

もし最後の要素が指定数に満たない場合、足りない要素だけでまとめられます。

`Iterable<T>`

* [Iterable<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html)
* [Iterable<T>#chunked](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/chunked.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/generated/_Collections.kt#L1811)

`Iterable<T>#chunked`の返り値型は、`List<List<T>>`です。


`Sequence<T>`

* [Sequence<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html)
* [Sequence<T>#zipWithNext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/chunked.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/generated/_Sequences.kt#L1346)

`Sequence<T>#chunked`の返り値型は、`Sequence<List<T>>`です。

```kotlin
val list : List<Int> = (0..5).toList()
assert(list == listOf(0, 1, 2, 3, 4, 5))

val result : List<List<Int>> = list.chunked(3)
assert(result == listOf(listOf(0, 1, 2), listOf(3, 4, 5)))
```


もし最後の要素が指定数に満たない場合、足りない要素だけでまとめられます。

```kotlin
val list : List<Int> = (0..6).toList()
assert(list == listOf(0, 1, 2, 3, 4, 5, 6))

val result : List<List<Int>> = list.chunked(3)
assert(result == listOf(listOf(0, 1, 2), listOf(3, 4, 5), listOf(6)))
```

引数に関数オブジェクトをとり、`List<T>`をさらに変換させることも可能です。


```kotlin
val list : List<Int> = (0..6).toList()
assert(list == listOf(0, 1, 2, 3, 4, 5, 6))

val result : List<Double> = list.chunked(3) { it.average() }
assert(result == listOf(1.0, 4.0, 6.0))
```


内部で次に紹介する`windowed`関数を呼び出しています。


# windowed : まとめる数と移動数を指定し、要素をまとめたList・Sequenceを生成する

`Iterable<T>`と`Sequence<T>`の拡張関数として、windowedという「まとめる数と移動数を指定し、要素をまとめたList・Sequenceを生成する」関数が追加されました。

いくつか引数を持ちます。

size : Int => まとめる数

まとめる数は0より大きくないといけません。0以下を渡すと例外を投げます。

```kotlin
val list : List<Int> = (0..3).toList()
assert(list == listOf(0, 1, 2, 3))

assert(list.windowed(2) == listOf(listOf(0, 1), listOf(1, 2), listOf(2, 3)))
assert(list.windowed(3) == listOf(listOf(0, 1, 2), listOf(1, 2, 3)))

assert(list.windowed(4) == listOf(listOf(0, 1, 2, 3)))
assert(list.windowed(1) == listOf(listOf(0), listOf(1), listOf(2), listOf(3)))
assert(list.windowed(5) == emptyList<Int>())
```

step : Int => 移動数。デフォルトは1。

移動数は0より大きくないといけません。0以下を渡すと例外を投げます。

sizeとstepはともにIntなので、名前付き引数呼び出しを使うことをお勧めします。

```kotlin
val list : List<Int> = (0..4).toList()
assert(list == listOf(0, 1, 2, 3, 4))

assert(list.windowed(size = 2, step = 1) == listOf(listOf(0, 1), listOf(1, 2), listOf(2, 3), listOf(3, 4)))
assert(list.windowed(size = 2, step = 2) == listOf(listOf(0, 1), listOf(2, 3)))
assert(list.windowed(size = 2, step = 3) == listOf(listOf(0, 1), listOf(3, 4)))

assert(list.windowed(size = 3, step = 1) == listOf(listOf(0, 1, 2), listOf(1, 2, 3), listOf(2, 3, 4)))
assert(list.windowed(size = 3, step = 2) == listOf(listOf(0, 1, 2), listOf(2, 3, 4)))
assert(list.windowed(size = 3, step = 3) == listOf(listOf(0, 1, 2)))
```

partialWindows : Boolean => まとまった結果がまとめる数より小さかった場合の処理の違い。デフォルトはfalse

```kotlin
val list : List<Int> = (0..4).toList()
assert(list == listOf(0, 1, 2, 3, 4))

assert(list.windowed(size = 2, step = 1, partialWindows = false) == listOf(listOf(0, 1), listOf(1, 2), listOf(2, 3), listOf(3, 4)))
assert(list.windowed(size = 2, step = 1, partialWindows = true) == listOf(listOf(0, 1), listOf(1, 2), listOf(2, 3), listOf(3, 4), listOf(4)))

assert(list.windowed(size = 2, step = 2, partialWindows = false) == listOf(listOf(0, 1), listOf(2, 3)))
assert(list.windowed(size = 2, step = 2, partialWindows = true) == listOf(listOf(0, 1), listOf(2, 3), listOf(4)))

assert(list.windowed(size = 2, step = 3, partialWindows = false) == listOf(listOf(0, 1), listOf(3, 4)))
assert(list.windowed(size = 2, step = 3, partialWindows = true) == listOf(listOf(0, 1), listOf(3, 4)))


assert(list.windowed(size = 3, step = 1, partialWindows = false) == listOf(listOf(0, 1, 2), listOf(1, 2, 3), listOf(2, 3, 4)))
assert(list.windowed(size = 3, step = 1, partialWindows = true) == listOf(listOf(0, 1, 2), listOf(1, 2, 3), listOf(2, 3, 4), listOf(3, 4), listOf(4)))

assert(list.windowed(size = 3, step = 2, partialWindows = false) == listOf(listOf(0, 1, 2), listOf(2, 3, 4)))
assert(list.windowed(size = 3, step = 2, partialWindows = true) == listOf(listOf(0, 1, 2), listOf(2, 3, 4), listOf(4)))

assert(list.windowed(size = 3, step = 3, partialWindows = false) == listOf(listOf(0, 1, 2)))
assert(list.windowed(size = 3, step = 3, partialWindows = true) == listOf(listOf(0, 1, 2), listOf(3, 4)))
```


`Iterable<T>`

* [Iterable<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html)
* [Iterable<T>#windowed](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/windowed.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/generated/_Sequences.kt#L1544)

`Iterable<T>#windowed`の返り値型は、`List<List<T>>`です。

`Sequence<T>`

* [Sequence<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html)
* [Sequence<T>#windowed](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/windowed.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/generated/_Sequences.kt#L1544)

`Sequence<T>#windowed`の返り値型は、`Sequence<List<T>>`です。

引数に関数オブジェクトをとり、`List<T>`をさらに変換させることも可能です。


```kotlin
val list : List<Int> = (0..4).toList()
assert(list == listOf(0, 1, 2, 3, 4))

val result = list.windowed(size = 2, step = 1) { it.average() }
assert(result == listOf(0.5, 1.5, 2.5, 3.5))
```

先に紹介した`chunked`は、次のようにwindowedを使い実装されています。

```kotlin
@SinceKotlin("1.2")
public fun <T> Iterable<T>.chunked(size: Int): List<List<T>> {
    return windowed(size, size, partialWindows = true)
}
```

```kotlin
@SinceKotlin("1.2")
public fun <T> Iterable<T>.chunked(size: Int): List<List<T>> {
    return windowed(size, size, partialWindows = true)
}
```


次の呼び出しは、chunkedと同じ結果になります。

```kotlin
val list : List<Int> = (0..4).toList()
assert(list == listOf(0, 1, 2, 3, 4))

assert(list.chunked(2) == list.windowed(size = 2, step = 2, partialWindows = true))
```

# fill : 指定要素で要素を全部埋め変える

`MutableList<T>`の拡張関数として、「指定要素で要素を全部埋め変える」fillが追加されました。

ドキュメント

* [MutableList<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/fill.html)
* [fill](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/fill.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/MutableCollections.kt#L295)

関数の返り値型はUnitです。

内部では、java.util.Collectionsの[fill](https://docs.oracle.com/javase/jp/6/api/java/util/Collections.html)メソッドを用いています。

```kotlin
val mutableList : MutableList<Int> = (0..4).toMutableList()
assert(mutableList == mutableListOf(0, 1, 2, 3, 4))

val unit : Unit = mutableList.fill(0) // 返り値型はUnit
assert(mutableList == mutableListOf(0, 0, 0, 0, 0))
```

各種Array系のクラスにも[fill](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/fill.html)という拡張関数が存在します。

# shuffle : 要素を並び替える

`MutableList<T>`の拡張関数として、「要素を並び替える」shuffuleが追加されました。

* [MutableList<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/fill.html)
* [shuffle](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/shuffle.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/MutableCollections.kt#L295)

引数として、追加で[java.util.Randomをとるオーバーロード](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/MutableCollections.kt#L316)も存在します。

内部では、java.util.Collectionsの[shuffule](https://docs.oracle.com/javase/jp/6/api/java/util/Collections.html#shuffle(java.util.List))メソッドを用いています。

```kotlin
val mutableList : MutableList<Int> = (0..4).toMutableList()
assert(mutableList == mutableListOf(0, 1, 2, 3, 4))

val unit : Unit = mutableList.shuffle(java.util.Random(0)) // 返り値型はUnit    
assert(mutableList == mutableListOf(4, 2, 1, 3, 0))
```

この関数は副作用をもち、返り値型はUnitなことがポイントです。副作用がなく、返り値型が`List<T>`のshuffledと正しく使い分けてください。

# shuffled : 要素を並び替えたリストを新たに生成する

`Iterable<T>`の拡張関数として、「要素を並び替えたリストを新たに生成する」shuffledが追加されました。

この関数は副作用をもちません。そして返り値型はList<T>なことがポイントです。新たにシャッフルされたList<T>が生成されます。

「あれ、shuffledを呼び出したのに、シャッフルされていない」ということがないように気を付けてください。

* [Iterable<T>](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html)
* [shuffled](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/shuffled.html)
* [ソース](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/MutableCollections.kt#L325)

引数として、追加で[java.util.Randomをとるオーバーロード](https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/MutableCollections.kt#L316)も存在します。

内部では、java.util.Collectionsの[shuffule](https://docs.oracle.com/javase/jp/6/api/java/util/Collections.html#shuffle(java.util.List))メソッドを用いています。

```kotlin
val list : List<Int> = (0..4).toList()
assert(list == listOf(0, 1, 2, 3, 4))

val shuffuled : List<Int> = list.shuffled(java.util.Random(0)) 
assert(shuffuled == listOf(4, 2, 1, 3, 0))
assert(list == listOf(0, 1, 2, 3, 4))
```

# [要注意] replaceAll : 全要素を関数オブジェクトで指定した条件で入れ替える

Java8でList<T>にデフォルト実装されている[replaceAll](https://docs.oracle.com/javase/jp/8/docs/api/java/util/List.html#replaceAll-java.util.function.UnaryOperator-)がKotlin1.2でも、MutableList<T>で呼び出せるようになりました。

JRE8限定です。JVMでもJRE6やJRE7、またはJavaScriptなどではコンパイルエラーになることに気を付けてください。

Kotlin 1.2で追加されたJRE8限定の関数として、[Map#getOrDefault](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/get-or-default.html)もあります。

# まとめ

Kotlin 1.2で追加されたコレクション系の関数を紹介しました。

「あ、あったらいいな」・「お、これ便利だな」という関数があったのではないでしょうか？

筆者は、この記事の執筆の前の週に業務において、zipWithNextを使いたい場面がありました。

また、Kotlin 1.2でStandard Libaryにおいて、`kotlin.coroutines.experimental`が使われていることにも注目です。

Kotlin Standary Libary、正しく使いこなしたいですね。