filter関数じゃなくてfilterTo関数使おうという話。
Twitterでの話題のサルベージです。
Kotlinはver1.0.4現在の話です。
発端
Kotlin の Set で filter したら List が返ってきて contains がくっそ重くなる事案が現実に起きてしまった……
— がくぞ (@gakuzzzz) 2016年9月23日
@yy_yank
— がくぞ (@gakuzzzz) 2016年9月23日
fun main(args: Array<String>) {
println(setOf(1, 2, 3, 4, 5, 6, 7, 8).filter { it % 2 == 0 } + (4))
}
とかやると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関数を使おう
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 件のコメント:
今更この話題を見つけたのですが、これ、あまりよくない話ではないかなーと思いちょっとコメントしてみることにしました。
がくぞさんの元の意図としては、immutableなSetを生成し、それをfilterしてimmutable Setを生成したかったことだと思われるのに対して、filterTo関数を使った解法では、*mutable*なSet(型として)が返ってきてしまって、型としてもmutableになっているので本質的な解決になっていないように感じます。
kmizushimaさん
確かにread onlyなSetからmutableなSetに型が変わってしまっているのは微妙ではありますね、
fiilterToがMutableCollectionですからねぇ・・。
asSequence使うのが一番良いのかもしれません。
コメントを投稿