【问题标题】:How to implement equals/hashcode in kotlin?如何在 kotlin 中实现 equals/hashcode?
【发布时间】:2020-02-21 00:24:28
【问题描述】:

我在 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
    }

但是当我尝试等于两个 List 时它不能正常工作。

PS:Group和Data类都是“数据类”

【问题讨论】:

  • 如果两个GroupWithDatas 有不同类型的Lists(例如ArrayListLinkedList)但以相同的顺序保存相同的元素,你会认为它们相等吗?
  • 是的,我认为这个列表是相等的
  • 嗯,equals() for Lists 的通常实现应该做你想做的事。 (假设你没有一些奇怪的实现会破坏它……)Data 绝对是data class?它有哪些属性类型? (如果任何不是原语或字符串,那些会被比较为不相等吗?)
  • 我想指出,在调试中,列表 1 (List) 的每个元素都等于列表 2 (List) 如果迭代它们但由于某种原因列表本身不是,现在这对我来说是最难以理解的事情不知何故是因为我从房间数据库中检索数据?
  • 实际的列表类是什么? (List 是一个接口,因此它需要是某个实现该接口的类。它应该 实现 equals() 以比较内容——或者,更有可能,继承自 AbstractList确实如此。但如果不是,那将解释您的问题。)

标签: kotlin equals


【解决方案1】:

TL;DR 检查data class Data 的内容:它是否覆盖equalsData 类的任何成员是否属于非原始类型并覆盖 equals?如需更多解释和示例,请进一步阅读。

如何在kotlin中实现equals/hashcode?​​h2>

你是绝对正确的,对于常规类,我们必须重写 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 文档,它说:

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

Documentation reference.

结构平等被描述为here

现在,这是更改后的 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,这也可能是问题所在。以此类推。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 2014-10-12
    相关资源
    最近更新 更多