如何在 Kotlin 中实现 equals/hashcode?

3

我在寻找有关Kotlin中equals/hashcode的信息,但遇到了困难。

对于数据类,我知道equals和(==)可以很好地使用,但对于普通类,我认为我们应该重写equals和hashcode方法:

class GroupWithData {

    var group: Group? = null

    var data: List<Data>? = null


    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as GroupWithData

        if (group!= other.group) return false
        if (data!= other.data) return false

        return true
    }

    override fun hashCode(): Int {
        var result = group?.hashCode() ?: 0
        result = 31 * result + (data?.hashCode() ?: 0)
        return result
    }

但是当我尝试将两个列表进行比较时,操作并不正确。

PS:Group和Data类均为“数据类”。


如果两个 GroupWithData 拥有不同类型的 List(例如一个 ArrayList 和一个 LinkedList),但是按照相同顺序持有相同元素,您是否认为它们相等? - gidds
是的,我认为这些列表将是相等的。 - Roab
实际的列表类是什么?(List是一个接口,因此它需要实现该接口的某个类。它应该实现equals()方法来比较内容 - 或者更可能是继承自例如AbstractList的类。但如果它没有这样做,那就可以解释你的问题。) - gidds
它是一个ArrayList。 - Roab
它们都是 Kotlin 集合中的 ArrayList。 - Roab
显示剩余3条评论
1个回答

3

TL;DR 检查data class Data的内容:它是否覆盖了equals函数?Data类的任何成员是否是非基本类型并覆盖了equals函数?阅读更多解释和示例。

如何在Kotlin中实现equals/hashcode函数?

您是完全正确的,对于常规类,如果我们想通过值而不是引用来比较两个对象,则必须重写equalshashCode函数。 equals函数的实现可以是您喜欢的任何内容。这完全取决于应用程序的业务逻辑和在您的特定情况下被视为“相等”的内容。

您的实现实际上已经足够好了。基本上检查标识(也称为按引用比较,=== Kotlin运算符),检查other是否是相同的实例类,其他任何内容都取决于业务逻辑。

hashCode函数的实现可以使用您在equals函数中比较的相同成员,但是使用类的其他成员也可以。

注意:通常认为最好同时重写equalshashCode函数。即使hashCode在您的代码中没有直接使用,它也可以在您使用的某些库的实现中使用。如果两个对象相等,则它们的哈希码也必须相等。如果对象未更改,则哈希码必须保持不变。

为什么两个列表可能不相等?

我不确定,但问题可能出在Data数据类或其成员的值中。

我运行了两个简单的测试,一个是基本类型,另一个是类似于以下内容的data class Data

测试#1

    var list1: List<Int> = listOf(1,2,3,4)
    var list2: List<Int> = arrayListOf(1,2,3,4)
    var list3: List<Int> = LinkedList<Int>().also { it.addAll(listOf(1,2,3,4)) }

    fun equals(): Boolean {
        return list1 == list3 && list1 == list2 && list2 == list3
    }

    println(equals())

测试 #2

    data class Data(var i: Int = 0)
    var list1: List<Data> = listOf(Data(1),Data(2),Data(3),Data(4))
    var list2: List<Data> = arrayListOf(Data(1),Data(2),Data(3),Data(4))
    var list3: List<Data> = LinkedList<Data>().also { it.addAll(listOf(Data(1),Data(2),Data(3),Data(4))) }

    fun equals(): Boolean {
        return list1 == list3 && list1 == list2 && list2 == list3
    }

    println(equals())

在这两种情况下,打印输出都是true。这是因为Kotlin文档中指出:

如果两个列表具有相同的大小并且在相同位置上具有结构相等的元素,则认为它们是相等的。

参见文档引用。 结构相等性在此处有所描述。现在,这是修改后的Data类,并且在equals方法的实现中发生了一些奇怪的事情:
    data class Data(var i: Int = 0) {
        // Custom equals!
        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false

            other as Data

            return (i + other.i) % 2 == 1
        }
    }

如果您尝试比较按相同顺序添加Data对象的相同列表,则这些列表不会相等,因为两个Data对象的比较有时会返回false。如果Data类包含另一种非基本类型,并且它的equals方法对于实际上相等的对象不返回true,那么这也可能是问题所在。等等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接