比较Kotlin中的两个列表

Asw*_*win 8 java kotlin kotlin-interop

我遇到了kotlin equals函数来比较两个相同类型的列表。它对于带有数据类的纯Kotlin很好用。

我正在Kotlin项目中使用Java库,其中的回调方法以X秒的时间间隔返回对象列表。尝试针对每个调用将旧列表与新列表进行比较,但是即使项目相同且相等,equals也会返回false。

val mOldList: MutableList<MyObject>()? = null

override fun updatedList(list: MutableList<MyObject>){
    // other code
    if (mOldList.equals(list)) // false everytime
}
Run Code Online (Sandbox Code Playgroud)

这是因为Java的来自库的equals方法吗?

列表比较的替代建议将是有益的。

Dru*_*uma 22

如果您不关心两个列表中元素的顺序,并且您的目标只是检查两个列表是否包含完全相同的元素,而没有其他任何元素,您可以考虑两个相互containsAll调用,例如:

var list1 = mutableListOf<String>()
var list2 = mutableListOf<String>()

if(list1.containsAll(list2) && list2.containsAll(list1)) {
    //both lists are of the same elements
}
Run Code Online (Sandbox Code Playgroud)

  • 如果列表中没有重复项,您可以先比较大小,然后一个 containsAll() 就足够了。 (5认同)
  • 我对此表示反对——这绝对不是最好的方法。 (4认同)
  • @DruidKuma,因为您基本上调用 containsAll 两次,而不是 O(n) 比较,这不会比每个索引的线性比较具有更好的性能。- 例如检查这个 - /sf/ask/713984071/ (4认同)
  • @shabunc 你能说明一下这种方式有什么问题吗? (3认同)
  • 应使用“Set”而不是“List”。```HashSet```` (不是 ```TreeSet``` - 这很重要)集合具有 O(1) 查找复杂度(列表具有 O(N))。因此只需将 ```mutableListOf``` 替换为 ```mutableSetOf``` 即可。 (2认同)

amy*_*nbe 13

使用zip

zip返回由该数组的元素和具有相同索引的另一个数组的元素构建的对列表。返回的列表具有最短集合的长度。

fun listsEqual(list1: List<Any>, list2: List<Any>): Boolean {

    if (list1.size != list2.size)
        return false

    val pairList = list1.zip(list2)

    return pairList.all { (elt1, elt2) ->
        elt1 == elt2       
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 此方法分配几个不必要的对象:最长列表中的每个元素至少有一个新列表和新的 Pair。此外还分配了几个枚举器,但 JIT 可以将它们放在堆栈上。所以这个方法需要 O(N) 堆中额外的内存。 (3认同)

Ren*_*ari 11

仅供参考list1 == list2,如果您的自定义对象基于 a data class(它会自动为您覆盖 equals),则无需任何额外工作即可调用。

  • 如果元素没有相同的顺序,这将不起作用,一个选项是在比较它们之前对两个列表进行排序。 (14认同)
  • @SalimMazariBoufares 排序和检查是完全错误的方法。假设我们按 id 排序,并且“A1”和“A2”具有相同的 id。排序后,`list1 = [A1, A2]` 这个列表仍然是 `[A1, A2]`。`list1 = [A2, A1]` 排序后列表仍然是 `[A2, A1]` 并且如果 `equals` 方法包含除 id 之外的任何其他参数,那么你就完蛋了。 (2认同)

XII*_*-th 7

您可以使用下面的实现来比较两个Collection

infix fun <T> Collection<T>.deepEqualTo(other: Collection<T>): Boolean {
    // check collections aren't same
    if (this !== other) {
        // fast check of sizes
        if (this.size != other.size) return false
        val areNotEqual = this.asSequence()
            .zip(other.asSequence())
            // check this and other contains same elements at position
            .map { (fromThis, fromOther) -> fromThis == fromOther }
            // searching for first negative answer
            .contains(false)
        if (areNotEqual) return false
    }
    // collections are same or they are contains same elements with same order
    return true
}
Run Code Online (Sandbox Code Playgroud)

或订购忽略变体:

infix fun <T> Collection<T>.deepEqualToIgnoreOrder(other: Collection<T>): Boolean {
    // check collections aren't same
    if (this !== other) {
        // fast check of sizes
        if (this.size != other.size) return false
        val areNotEqual = this.asSequence()
            // check other contains next element from this
            .map { it in other }
            // searching for first negative answer
            .contains(false)
        if (areNotEqual) return false
    }
    // collections are same or they are contains same elements
    return true
}
Run Code Online (Sandbox Code Playgroud)

注意:两个函数只比较深度的第一级

  • 第二个答案的复杂度为 O(N^2)。语句“it in other”对于列表来说具有 O(N) 复杂度,并且被调用 N 次。第二种情况的正确解决方案类似于 ```return this.toSet() == other.toSet()``` (2认同)
  • @ManushinIgor 是的,你的解决方案比我的更好。谢谢 (2认同)

Dak*_*rra 6

Java列表实现equals方法,并且如果两个列表包含相同顺序的相同元素,则两个列表定义为相等。我想,您equalsMyObject课堂上缺少方法。


joe*_*cks 6

:使用扩展功能的简短版本:

fun List<*>.deepEquals(other : List<*>) = 
    this.size == other.size && this.mapIndexed { index, element -> element == other[index] }.all { it }
Run Code Online (Sandbox Code Playgroud)

你可以这样使用它:

listOf("Hola", "Mundo").deepEquals(listOf("Hello", "World"))
Run Code Online (Sandbox Code Playgroud)

  • 为了尽早中断并避免迭代整个列表,您应该使用 `this.asSequence().mapIndexed {...}` (2认同)

Vir*_*ker 0

您可以迭代一个列表并检查第二个列表中相应的位置值。以下面的例子供参考。

var list1 = mutableListOf<String>()
var list2 = mutableListOf<String>()

list1.forEachIndexed { i, value ->
    if (list2[i] == value)
    {
        // your implementaion
    }  
}
Run Code Online (Sandbox Code Playgroud)

此外,您可以过滤列表以查找更改的值。

var list1 = mutableListOf<String>()
var list2 = mutableListOf<String>()

val changedList = list1.filterIndexed { i, value -> 
    list2[i] != value)
}
Run Code Online (Sandbox Code Playgroud)

  • 如果列表的大小不同,此方法可能会失败。此方法还在内存中分配新列表。 (2认同)