67
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

KotlinAdvent Calendar 2017

Day 4

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

Last updated at Posted at 2017-12-03

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

Kotlin 1.2でも、Standard Libaryにいくつかの追加があります。

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

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

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

Iterable<T>

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

Sequence<T>

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

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))

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

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

利用例としてとても分かりやすので、ぜひ一度ソースコードを見てみることをおすすめします。

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>#chunkedの返り値型は、List<List<T>>です。

Sequence<T>

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

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)))

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

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>をさらに変換させることも可能です。

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以下を渡すと例外を投げます。

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なので、名前付き引数呼び出しを使うことをお勧めします。

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

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>#windowedの返り値型は、List<List<T>>です。

Sequence<T>

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

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

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を使い実装されています。

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

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

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が追加されました。

ドキュメント

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

内部では、java.util.Collectionsのfillメソッドを用いています。

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という拡張関数が存在します。

shuffle : 要素を並び替える

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

引数として、追加でjava.util.Randomをとるオーバーロードも存在します。

内部では、java.util.Collectionsのshuffuleメソッドを用いています。

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なことがポイントです。新たにシャッフルされたListが生成されます。

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

引数として、追加でjava.util.Randomをとるオーバーロードも存在します。

内部では、java.util.Collectionsのshuffuleメソッドを用いています。

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にデフォルト実装されているreplaceAllがKotlin1.2でも、MutableListで呼び出せるようになりました。

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

Kotlin 1.2で追加されたJRE8限定の関数として、Map#getOrDefaultもあります。

まとめ

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

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

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

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

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

67
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
67
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?