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
- key对象实现hashCode() fun
- 关键对象实现equals() fun
- 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
的值
我正在使用 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
- key对象实现hashCode() fun
- 关键对象实现equals() fun
- 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
的值