アイリッジ開発者ブログ

アイリッジに所属するエンジニアが技術情報を発信していきます。

【Android】便利なCollection関数

こんにちは。

開発部第1グループ、Android担当の沓名です。

Androidの開発において、言語としてKotlinを選択することは今やスタンダードになっています。

JavaからKotlinに変わり、色々と快適になったと思える要素はありますが、その中でも、各種Collection関数は本当に便利だと個人的に感じています。

その名の通り、リストやコレクションの各要素に対する操作を容易に行うために用意されている関数ですが、とにかく数が豊富で様々なシチュエーションに対応できるようになっています。

今回はそんなCollection関数について紹介できればと思います。

Collection関数の簡単な例


JavaでいうforEach文をイメージしていただけると分かりやすいかと思います。

例えば、以下のようなコードがあるとします。

List<String> list = Arrays.asList("ab", "cde", "fg", "hij");
list.forEach(s -> {
    if(s.length() == 3) {
        System.out.println(s)
    }
});

>> 出力結果:
cde
hij

文字列のリストから3文字の要素のみ出力する、という簡単なコードです。

これをKotlinで置き換えると以下のようになります。

val list = listOf("ab", "cde", "fg", "hij")
list.filter { s -> s.count() == 3 }.forEach {
    println(it)
 }

>> 出力結果:
cde
hij

極端な例ではありますが、シンプルにすることができました。

この例ではfilterとforEachをリストに対して使用して出力を行っています。

このような関数がKotlinにはたくさん用意されています。

Collection関数の全貌


実際、どのようなCollection関数があるのか。

復習も兼ねて改めて調べてみたので、以下に記載しています。

とても数が多いので、興味のある方のみ読んでみてください。

Kotlinのバージョンアップで追加された関数は意外と見落としがちなので、赤字にしてあります。

※順番はKotlinの_Collections.ktに記載されている順なので結構めちゃくちゃです。

1. elementAt

elementAt, elementAtOrElse, elementAtOrNull

2. find

find, findLast

3. first

first, firstNotNullOf(Since: Kotlin 1.5), firstNotNullOfOrNull(Since: Kotlin 1.5),firstOrNull

4. getOrElse

5. getOrNull

6. indexOf

indexOf, indexOfFirst, indexOfLast

7. last

last, lastIndexOf, lastOrNull

8. random

random(Since: Kotlin 1.3), randomOrNull(Since: Kotlin 1.4)

9. single

single, singleOrNull

10. drop

drop, dropLast, dropLastWhile, dropWhile

11. filter

filter, filterIndexed, filterIndexedTo, filterIsInstance,

filterIsInstanceTo, filterNot, filterNotNull, filterNotNullTo,

filterTo

12. slice

12. take

take, takeLast, takeLastWhile, takeWhile

13. reverse

reverse, reversed

14. shuffle(Since: Kotlin 1.3)

15. sort

sortBy, sortByDescending, sortDescending,

sorted, sortedBy, sortedByDescending,

sortedDescending, sortedWith

16. to◯◯Array

toBooleanArray, toByteArray, toCharArray,

toDoubleArray, toFloatArray, toIntArray,

toLongArray, toShortArray

17. associate

associate, associateBy, associateByTo, associateTo,

associateWith(Since: Kotlin 1.3), associateWithTo(Since: Kotlin 1.3)

18. to◯◯

toCollection, toHashSet, toList,

toMutableList, toSet, toMutableSet

19. flatMap(Since: Kotlin 1.4)

flatMap, flatMapIndexed, flatMapIndexedTo, flatMapTo

20. groupBy

groupBy, groupByTo, groupingBy(Since: Kotlin 1.1)

21. map

map, mapIndexed, mapIndexedNotNull,

mapIndexedNotNullTo, mapIndexedTo, mapNotNull,

mapNotNullTo, mapTo

22. withIndex

23. distinct

distinct, distinctBy

24. intersect

25. subtract

26. union

27. all

28. any

29. count

30. fold

fold, foldIndexed, foldRight, foldRightIndexed

31. forEach

forEach, forEachIndexed

32. max

Kotlin1.4以降、以下のメソッドはDeplecate

max, maxBy, maxWith

代わりに1.4以降は以下のメソッドの使用が推奨

maxByOrNull(Since: Kotlin 1.4), maxOf(Since: Kotlin 1.4),

maxOfOrNull(Since: Kotlin 1.4), maxOfWith(Since: Kotlin 1.4),

maxOfWithOrNull(Since: Kotlin 1.4), maxOrNull(Since: Kotlin 1.4),

maxWithOrNull(Since: Kotlin 1.4)

33. min

Kotlin1.4以降、以下のメソッドはDeplecate

min, minBy, minWith

代わりに1.4以降は以下のメソッドの使用が推奨

minByOrNull(Since: Kotlin 1.4), minOf(Since: Kotlin 1.4),

minOfOrNull(Since: Kotlin 1.4), minOfWith(Since: Kotlin 1.4),

minOfWithOrNull(Since: Kotlin 1.4), minOrNull(Since: Kotlin 1.4),

minWithOrNull(Since: Kotlin 1.4)

34. none

35. onEach

onEach(Since: Kotlin 1.1), onEachIndexed (Since: Kotlin 1.4)

36. reduce

reduce, reduceIndexed,

reduceIndexedOrNull(Since: Kotlin 1.4), reduceOrNull(Since: Kotlin 1.4),

reduceRight, reduceRightIndexed,

reduceRightIndexedOrNull(Since: Kotlin 1.4), reduceRightOrNull(Since: Kotlin 1.4)

37. runningFold (Since: Kotlin 1.4)

runningFold, runningFoldIndexed

38. runningReduce(Since: Kotlin 1.4)

runningReduce, runningReduceIndexed

39. scan(Since: Kotlin 1.4)

scan, scanIndexed

40. sum

sum, sumOf(Since: Kotlin 1.4)

以下はkotlin1.5からDeprecate

sumBy, sumByDouble

41. requireNoNulls

42. chunked(Since: Kotlin 1.2)

43. minus

minus, minusElement

44. partition

45. plus

plus, plusElement

46. windowed(Since: Kotlin 1.2)

47. zip

zip, zipWithNext(Since: Kotlin 1.2, 1.4)

48. joinTo

joinTo, joinToString

49. as◯◯

asIterable, asSequence

50. average

抜粋


出来る限り全てのCollection関数を調べて記載しましたが、

多すぎるのでよく使うものやあまり使ったことがないけど使えそうなものを抜粋して紹介します。

1. map(transform: (T) -> R): List

こちらはリストの要素の値を操作してから別のリストにして返すメソッドです。

APIの返却値を格納したEntityクラスをModelクラスに変換する時などによく使います。

例:

data class Entity(val key: String, val value: String)
data class Model(val key: String, val value: String, keyValue: String)
val entityList = listOf(Entity("key1", "value1"), Entity("key2", "value2"))
val modelList = entityList.map { entity -> 
    Model(key = entity.key, 
                value = entity.value,
                keyValue = entity.key + entity.value) 
}
println(modelList)

>> 出力結果:
[Model(key=key1, value=value1, keyValue=key1value1), 
Model(key=key2, value=value2, keyValue=key2value2)]

2. filter(predicate: (T) -> Boolean): List

冒頭でも例に上げましたが、こちらはリストの各要素から、指定した条件に当てはまる要素のみを抜き出してリストにして返すメソッドです。

リストから特定の条件の要素のみ抜き出したい時などによく使います。

例:

data class Entity(val key: String, val value: String, val isPrintFlag: Boolean)
val entityList = listOf(Entity("key1", "value1", true), 
                                                Entity("key2", "value2", false), 
                                                Entity("key3", "value3", true), 
                                                Entity("key4", "value4", false))
val filteringList = entityList.filter{ it.isPrintFlag }

println(filteringList)

>> 出力結果:
[Entity(key=key1, value=value1, isPrintFlag=true), 
Entity(key=key3, value=value3, isPrintFlag=true)]

3. sortedBy(crossinline selector: (T) -> R?): List

こちらは条件を指定して昇順に並び替えたリストを返すメソッドです。

リストを特定の要素の順番に並び替えたいときなどによく使用します。

例:

data class Entity(val key: String, val value: String, val sortNum: Int)
val entityList = listOf(Entity("key1", "value1", 4), 
                                                Entity("key2", "value2", 2), 
                                                Entity("key3", "value3", 3), 
                                                Entity("key4", "value4", 1))
val sortedList = entityList.sortedBy{ it.sortNum }

println(sortedList)

>> 出力結果:
[Entity(key=key4, value=value4, sortNum=1), 
Entity(key=key2, value=value2, sortNum=2), 
Entity(key=key3, value=value3, sortNum=3), 
Entity(key=key1, value=value1, sortNum=4)]

4. union(other: Iterable): Set

引数で指定したリストの要素と元のリストの要素を集め、重複を削除したSetにして返すメソッドです。

ほぼ使ったことはありませんが、異なる2つのリストの要素からmapなどで特定の要素を抜き出したリストをそれぞれ作り、重複を削除した上で合体させたい時などに使えそうです。

例:

data class Entity(val key: String, val value: String)
val entityList1 = listOf(Entity("key1", "北海道"), 
                                                Entity("key2", "東京"), 
                                                Entity("key3", "沖縄"), 
                                                Entity("key4", "アメリカ"),
                                                Entity("key5", "カナダ"))

val entityList2 = listOf(Entity("key6", "神奈川"), 
                                                Entity("key7", "長野"), 
                                                Entity("key8", "アメリカ"), 
                                                Entity("key9", "東京"),
                                                Entity("key10", "大阪"))

println(entityList1.map { it.value }.union(entityList2.map { it.value }))

>> 出力結果:
[北海道, 東京, 沖縄, アメリカ, カナダ, 神奈川, 長野, 大阪]

5. zip(other: Array): List<Pair<T, R>>

こちらは2つのリストからPairのリストを生成するメソッドです。

こちらもあまり使ったことはありませんが、関連がありつつもデータ構造の異なる2つのリストを一纏めにしておきたい時に使えそうです。

例:

data class BasicData(val key: String, val value: String)
data class AuthData(val accessToken: String, val refreshToken: String)
val basicList = listOf(BasicData("key1", "value1"), 
                                                BasicData("key2", "value2"), 
                                                BasicData("key3", "value3"))
val authList = listOf(AuthData("accessToken1", "refreshToken1"), 
                                                AuthData("accessToken2", "refreshToken2"), 
                                                AuthData("accessToken3", "refreshToken3"))
val zipList = basicList.zip(authList)
println(zipList)

>> 出力結果:
[(BasicData(key=key1, value=value1), 
    AuthData(accessToken=accessToken1, refreshToken=refreshToken1)), 
(BasicData(key=key2, value=value2), 
    AuthData(accessToken=accessToken2, refreshToken=refreshToken2)), 
(BasicData(key=key3, value=value3), 
    AuthData(accessToken=accessToken3, refreshToken=refreshToken3))]

まとめ


改めて調べてみて分かりましたが、Collection関数は本当に種類が豊富です。

Kotlinのバージョンアップで新機能が追加されることも多いので、要チェックです。

Collection関数を上手く使えば、複雑なListの操作であってもかなりシンプルに書けることがわかります。

「ここの記述、少し冗長だな」と思ったときはCollection関数でシンプルに出来ないか考える癖をつければ、

可読性の高い簡潔なプログラムを作ることに繋がることかと思います。

今回は以上になります。

皆様、良いKotlinライフを…。

参考URL


  • Kotlinのコレクションの使い方メモ

https://qiita.com/opengl-8080/items/36351dca891b6d9c9687

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

https://developer.android.com/about/versions/12/behavior-changes-12?hl=ja