2016/09/24 このエントリーをはてなブックマークに追加 はてなブックマーク - Kotlinのfilter関数はArrayListを返すので注意

Kotlinのfilter関数はArrayListを返すので注意

filter関数じゃなくてfilterTo関数使おうという話。
Twitterでの話題のサルベージです。


Kotlinはver1.0.4現在の話です。





Kotlin標準のfilter関数ですが、こんな感じ。
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter.html

実際の拡張関数はこんな感じになっています。

/**
 * Returns a list containing only elements matching the given [predicate].
 */
public inline fun <T> Array<out T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

Iterableなクラスのfilter関数の戻り値はArrayListという罠。(というかAPIリファレンスをちゃんと読もうという話ではある)

先程のがくぞさんの例を用いるとこういうことが起こります。

fun main(args: Array<String>) {
    // setOfで作られるのはMutableSetである
    println(setOf(1, 2, 3, 4, 5, 6, 7, 8) is MutableSet<Int>)
    // filterが返すのはjava.util.ArrayListである
    println(setOf(1, 2, 3, 4, 5, 6, 7, 8).filter { it % 2 == 0 } is java.util.ArrayList)
    // 要素に4を足したとき、Setだと思ってたのにArrayListなのでaddされてしまう
    println(setOf(1, 2, 3, 4, 5, 6, 7, 8).filter { it % 2 == 0 } + (4))
}
出力結果
true
true
[2, 4, 6, 8, 4]

filterTo関数の実装はこんな感じです。filterの戻り値を明示的に指定する。

public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

もしくはasSequence()でSequenceとして扱うかですね。
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/as-sequence.html

参考)
Sequenceの話 ~kotlin.sequences.Sequence~ by RyotaMurohoshi

実際に書くとこんな感じ。

fun main(args: Array<String>) {
    println(setOf(1, 2, 3, 4, 5, 6, 7, 8).filterTo(mutableSetOf(), {it % 2 == 0 }) is java.util.ArrayList<*>)
    // 要素に4を足したとき、Setなのでaddされない
    println(setOf(1, 2, 3, 4, 5, 6, 7, 8).filterTo(mutableSetOf(), {it % 2 == 0 }) + (4))
}



出力結果


false
[2, 4, 6, 8]


以上!!!



2 件のコメント:

  1. 今更この話題を見つけたのですが、これ、あまりよくない話ではないかなーと思いちょっとコメントしてみることにしました。

    がくぞさんの元の意図としては、immutableなSetを生成し、それをfilterしてimmutable Setを生成したかったことだと思われるのに対して、filterTo関数を使った解法では、*mutable*なSet(型として)が返ってきてしまって、型としてもmutableになっているので本質的な解決になっていないように感じます。

    返信削除
    返信
    1. kmizushimaさん

      確かにread onlyなSetからmutableなSetに型が変わってしまっているのは微妙ではありますね、
      fiilterToがMutableCollectionですからねぇ・・。

      asSequence使うのが一番良いのかもしれません。

      削除