/var/log/jsoizo

メモ帳 技術とか趣味とか

Iterable<T>.firstNotNullOfOrNullはぱっとみよくわからないけどとても便利

scalaでいうcollectFirst的なやつが欲しいなーと思っていたところ、
firstNotNullOfOrNullっていうやーつがメソッド名だけ読んで理解するのが困難だがcollectFirst的に使えて便利だったのでメモ。

シグネチャは以下通り

public inline fun <T, R : Any> Iterable<T>.firstNotNullOfOrNull(transform: (T) -> R?): R?

配列の各要素に対して、引数に与えた変換処理transformを実行し、

  • transformの結果が最初にnullでないものが見つかればそれを返す
  • 配列の中に一つも条件を満たすものがなければnullを返す

というもの。

例えば適当な乱数とインデックスを持った配列から最初に9の倍数が出てきた場所を探したい場合、このように書ける。

val list = (1..100).map{ Pair(it, Random.nextInt(0, 100)) }
val result = list.firstNotNullOfOrNull{ (i, rand) -> 
    if (rand % 9 == 0) i else null
}

こうすることもできるが配列を2回ループすることになり効率が悪い。

val result = list.mapNotNull{ (i, rand) -> 
    if (rand % 9 == 0) i else null
}.firstOrNull()

なおscalaだとこんなかんじ

val rand = new scala.util.Random
val list = (1 to 100).map((_, rand.nextInt(100)))
list.collectFirst{ case (i, rand) if rand % 9 == 0 => i }

ちなみに Iterable<T>.firstNotNullOf というのもあるが、これは Iterable<T>.firstNotNullOfOrNull でNullが返却される際に例外を投げる版。 例外から解放されて安全にnullを扱いたくてこういうメソッドを使っているわけなのでこれが必要な状況はあまり想像できない。