使用自定义等号函数在Kotlin中减去两个列表

57hvy0tb  于 2022-11-16  发布在  Kotlin
关注(0)|答案(4)|浏览(112)

我有两个List,我想得到的List只包含第一个列表中的元素,而第二个列表中没有。问题是,我需要在减法时指定一个自定义的equal。假设我想使用列表条目中的一个字段,比如id
我是这样实现的:

list1.filter { log -> list2.none { it.id == log.id } }

val projection = logEntries.map { it.id }
list1.filter { it.id !in projection }

有没有更好的方法?请考虑到我不能为类设置一个新的equal方法。

jfgube3f

jfgube3f1#

你这样做是可以的,但当列表变大时,你可能会想这样做:
通过首先将引用列表(list2)转换为集合,可以使该过程更高效。

val referenceIds = list2.distinctBy { it.id }.toSet()

list1.filterNot { it.id in referenceIds }

背景:

当你检查一个元素是否被包含时,你最有可能使用的ArrayList有一个O(n)的时间复杂度,所以,如果列表变大,它会花更长的时间。
HashSet的时间复杂度为O(1),因此,如果list2变大,它的速度不会变慢。

zvms9eto

zvms9eto2#

另一种方法?

fun main() {

    val list1 = listOf(0, 1, 2, 3, 4, 5)
    val list2 = listOf(2,3,4)

    println(list1.filterNotIn(list2))
}

fun <T> Collection<T>.filterNotIn(collection: Collection<T>): Collection<T> {
    val set = collection.toSet()
    return filterNot { set.contains(it) }
}

输出:[0, 1, 5]

u3r8eeie

u3r8eeie3#

正如评论所说,没有内置的方法来实现这一点。就因为没人觉得有这个必要。)
不过,你可以很容易地自己添加一个。例如,下面是你的第一个建议,转换为扩展函数:

fun <T, R> Collection<T>.minus(elements: Collection<T>, selector: (T) -> R?)
    = filter{ t -> elements.none{ selector(it) == selector(t) } }

然后,您可以使用与内置函数相同的方式调用此函数:

list1.minus(list2){ it.id }

(可能还有更高效的实现,但这说明了这个想法。)

jv4diomz

jv4diomz4#

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/minus.html

fun main() {

    val list1 = listOf(0, 1, 2, 3, 4, 5)
    val list2 = listOf(2,3,4)
    println(list1.minus(list2))
    
}

相关问题