HashMap returns 使用具有相同 hashCode 值且 equals = true 的当前键对象获取时为 null

HashMap returns null when fetching with present key object with same hashCode value and equals = true

我正在使用 Question 类型的对象作为 HashMap 中的键。我实现了 hashCode()equals 函数,如下所示。

Question.kt

@Parcelize
@Entity(primaryKeys = ["id"])
data class Question(
    val id: Int,
    val question:String
) : Parcelable{
    @Ignore
    @IgnoredOnParcel
    var isQuestionDimmed: Boolean = false

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

        other as Question

        if (id != other.id) return false
        if (isQuestionDimmed != other.isQuestionDimmed) return false

        return true
    }

    override fun hashCode(): Int {
        var result = id
        result = 31 * result + isQuestionDimmed.hashCode()
        return result
    }[![enter image description here][1]][1]
}

fun Question.clone(): Question {
    val question = Question(id,question)
    question.isQuestionDimmed = isQuestionDimmed
    return question
}

现在我这样创建 HashMap:

val hm:LinkedHashMap<Question, QuestionAnswerDetails> = hashMapOf()

现在,我希望当我尝试获取某个键的值时,HashMap 会在后台使用 hashCode() 和 equals() 函数来定位预先插入的值。

这总是有效的,除非我修改了键的“isQuestionDimmed”属性。然后,在那之后,始终获取该密钥 returns null!尽管密钥存在并且具有与我正在搜索的密钥对象相同的 hashCode 和 equals() 函数 returns true.

下面是我实际调试的屏幕截图:

在HashMaps中使用对象作为键时,必须确保满足以下条件:

更新感谢@cactustictacs

  1. key对象实现hashCode() fun
  2. 关键对象实现equals() fun
  3. hashCode() 和 equals() 实现中使用的关键对象属性是不可变的(不一定是整个关键对象是不可变的)

为什么要覆盖 equals() 和 hashCode()?

如果 class 没有覆盖对象 class 的 equals() 和 hashCode() 方法,并且这样的 class 的对象被用作映射的键​​或在 Java 中设置,使用这些方法的默认实现,它只检查引用相等性。 Reference 因此,您将只能使用与最初用于添加值的完全相同的键对象来访问地图值。

为什么 hashCode() 和 equals() 实现中使用的关键对象属性应该是不可变的?

因为,当首先定义值v1到k1时,分配给(k1,v1)在内存中的位置取决于第一次计算的hashcode (k1,v1)根据hashcode添加到hashMap的 k1。如果你后来修改了一个k1属性,它在hashCode()中使用,它的hashcode会变得不一样,但是(k1,v1)在内存中的位置会按照k1原来的hashcode保持不变。因此,如果您尝试在修改 hashmap 后使用 k1 获取它,结果将为 null,因为它将指向内存中的一个新位置,但没有分配任何内容。 equals() 也是一样,其中使用的属性应该是不可变的,以便能够在 HashMap 中只维护唯一的键。

如何修复上面的代码?

幸运的是,由于 Kotlin 的数据 classes 为您提供了仅使用主构造函数中的属性的 hashCode() 和 equals() 的默认实现,我们可以简单地重写代码如下:

@Parcelize
@Entity(primaryKeys = ["id"])
data class Question(
    val id: Int,
    val question:String
) : Parcelable{
    @Ignore
    @IgnoredOnParcel
    var isQuestionDimmed: Boolean = false
}

现在,您可以自由更改isQuestionDimmed的值