kotlin.collectionsの紹介〜初級編〜
こんにちは、株式会社イエソドのtsukakeiです
これはKotlin Advent Calendar 2021 15日目の記事です
みなさん、Kotlinは好きですか?私は大好きです!
Kotlinではコレクション系の便利メソッドを標準ライブラリとして提供してくれています
前回は初心者向けのメソッドを紹介しましたが、今回は初級編です
all・any・count
どんなときに使う?
コレクションに対してforEachしたくなったとき
all
は、渡したpredicateがコレクションの全要素に対して、trueかどうかを返します
val isEven: (Int) -> Boolean = { it % 2 == 0 }
val zeroToTen = 0..10
val allEven: Boolean = zeroToTen.all { isEven(it) } // false
any
は、渡したpredicateがコレクションのある要素に対して、trueになるものがあるかどうかを返します
val isEven: (Int) -> Boolean = { it % 2 == 0 }
val zeroToTen = 0..10
val anyEven: Boolean = zeroToTen.any { isEven(it) } // true
count
は、渡したpredicateがコレクションのある要素に対して、trueになる要素の個数を返します
val isEven: (Int) -> Boolean = { it % 2 == 0 }
val zeroToTen = 0..10
val numOfEven: Int = zeroToTen.count { isEven(it) } // 6
filterNotNull
どんなときに使う?
コレクションからnullを取り除いてほしいとき
filterNotNull
は、コレクションの各要素のうち、nullでない要素のみを収納したListを返します
fun main() {
val numbers: List<Int?> = listOf(1, 2, null, 4)
val nonNullNumbers = numbers.filterNotNull()
println(nonNullNumbers) // [1, 2, 4]
}
filter { it != null }
でも同じ挙動にはなりますが、意図が伝わりやすい & 型からNullableが取り除かれるので、nullを取り除きたい用途であればfilterNotNull
を使った方がいいです
val numbers: List<Int?> = listOf(1, 2, null, 4)
val nonNullNumbers: List<Int> = numbers.filterNotNull() // good
val maybeNonNullNumbers: List<Int?> = numbers.filter { it != null } // bad
mapNotNull
どんなときに使う?
コレクションを変換して、その結果からnullを取り除いてほしいとき
mapNotNull
は、渡したtransformをコレクションの各要素に対して実行し、nullを除いた結果を収納したListを返します
fun main() {
val strings: List<String> = listOf("12a", "45", "", "3")
val ints: List<Int> = strings.mapNotNull { it.toIntOrNull() }
println(ints) // [45, 3]
}
map { ... }.filterNotNull()
でも同じ結果にはなりますが、取り除きたいものはわざとnullにしているという意図が伝わりやすい & 短くて読みやすくなるので、mapNotNull
を使った方がいいです
val strings: List<String> = listOf("12a", "45", "", "3")
val ints1: List<Int> = strings.mapNotNull { it.toIntOrNull() } // better
val ints2: List<Int> = strings.map { it.toIntOrNull() }.filterNotNull() // good
findとfindLast
どんなときに使う?
コレクションから要素を探すとき
find
は、渡したpredicateをコレクションの 先頭 から各要素に対して実行し、最初にtrueになった要素(trueになる要素がなかった場合はnull)を返します
findLast
は、渡したpredicateをコレクションの 最後 から各要素に対して実行し、最初にtrueになった要素(trueになる要素がなかった場合はnull)を返します
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6, 7)
val firstOdd = numbers.find { it % 2 != 0 }
val lastEven = numbers.findLast { it % 2 == 0 }
println(firstOdd) // 1
println(lastEven) // 6
}
似たようなメソッド(というか実装は同じ)としては、firstOrNull(predicate: (T) -> Boolean)
とlastOrNull(predicate: (T) -> Boolean)
があります
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6, 7)
val firstOdd = numbers.firstOrNull { it % 2 != 0 }
val lastEven = numbers.lastOrNull { it % 2 == 0 }
println(firstOdd) // 1
println(lastEven) // 6
}
associate
・associateBy
・associateWith
)
associate系(どんなときに使う?
コレクションからMapを作りたいとき
コレクションからMapを生成したいときはままあるかと思います(計算量減らしたい時とか)
そのときに便利なのがassociate系のメソッドです
associate系にはassociate
・associateBy
・associateWith
の3種類ありますが、その使い分けは簡単です
コレクションの各要素から、
- MapのKeyもValueも作りたい →
associate
- MapのKeyだけ作りたい →
associateBy
- MapのValueだけ作りたい →
associateWith
となります
MapのKeyもValueも作りたい → associate
fun main() {
val names = listOf("田中 太郎", "鈴木 次郎", "佐藤 太郎")
val byLastName = names.associate { it.split(" ").let { (firstName, lastName) -> lastName to firstName } }
// "田中 太郎"は、あとの"佐藤 太郎"とkeyかぶりしたので上書きされて出てきません
println(byLastName) // {次郎=鈴木, 太郎=佐藤}
}
Keyだけ作りたい → associateBy
fun main() {
data class Person(val firstName: String, val lastName: String)
val names = listOf(Person("田中", "太郎"), Person("鈴木", "次郎"), Person("佐藤", "太郎"))
val byLastName = names.associateBy { it.lastName }
// "田中 太郎"は、あとの"佐藤 太郎"とkeyかぶりしたので上書きされて出てきません
println(byLastName) // {次郎=鈴木 次郎, 太郎=佐藤 太郎}
}
ちなみに、valueTransform
を指定することも一応できます
MapのValueだけ作りたい → associateWith
fun main() {
val words = listOf("a", "abc", "ab", "def", "abcd")
val withLength = words.associateWith { it.length }
println(withLength) // {a=1, abc=3, ab=2, def=3, abcd=4}
}
おわりに
今回は初級編ということで、基本的なメソッドを中心に取り上げてみました!
(associate系については、中級編と初級編の中間くらいで迷いましたが入れました)
次回は中級編をお届けしたいと思います
ありがとうございました!
Discussion