You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改Kotlin数据类属性后MutableMap.contains返回false问题

问题描述

我有一个MutableMap,其键为User数据类的对象,值为Dog数据类的列表。将User对象存入Map后,通过持有该对象的变量修改其属性,此时map的contains方法返回false——尽管Map的key已同步更新属性,且对象引用未发生变化。反向操作(从Map取出key修改后测试contains)也会出现同样问题。

示例代码:

data class User(
    var name: String,
    var project: String,
)

data class Dog(
    var kind: String
)


fun main(args: Array<String>) {
    var mapUserDogs: MutableMap<User, MutableList<Dog>> = mutableMapOf()
    var userSelected = User("name2", "P2")

    mapUserDogs.put(
        User("name1", "P1"),
        mutableListOf(Dog("R1"), Dog("R2"))
    )

    mapUserDogs.put(
        userSelected,
        mutableListOf(Dog("R21"), Dog("R31"))
    )

    println(userSelected)
    println(mapUserDogs.keys.toString())
    println(mapUserDogs.contains(userSelected))
    println(mapUserDogs.values.toString())
    println("\n")

    userSelected.name = "Name3"

    println(userSelected)
    println(mapUserDogs.keys.toString())
    println(mapUserDogs.contains(userSelected))
    println(mapUserDogs.values.toString())
}

运行打印结果:

User(name=name2, project=P2)
[User(name=name1, project=P1), User(name=name2, project=P2)]
true
[[Dog(kind=R1), Dog(kind=R2)], [Dog(kind=R21), Dog(kind=R31)]]


User(name=Name3, project=P2)
[User(name=name1, project=P1), User(name=Name3, project=P2)]
false
[[Dog(kind=R1), Dog(kind=R2)], [Dog(kind=R21), Dog(kind=R31)]]

Process finished with exit code 0

请问如何实现修改User对象属性后,Map的contains方法仍能返回true?


解决方案

出现这个问题的核心原因是:Kotlin的data class会自动基于所有属性生成equals()和hashCode()方法,而MutableMap(默认是HashMap)依赖这两个方法来定位键。当修改User的可变属性时,对象的hashCode会发生变化,导致Map无法在原来的哈希桶位置找到这个键,即使对象引用完全相同。

下面提供几种可行的解决办法:

1. 将User的属性设为不可变(推荐)

如果业务允许,直接把User的属性改为val,禁止修改:

data class User(
    val name: String,
    val project: String,
)

这样属性无法被修改,hashCode和equals的结果永远稳定,Map的contains方法就能正常工作。

2. 修改属性后重新更新Map

如果必须要修改属性,修改完成后需要将该键从Map中移除,再重新存入。这样Map会根据新的hashCode将键放到正确的哈希桶位置:

// 修改属性后执行以下代码
val dogs = mapUserDogs.remove(userSelected) ?: mutableListOf()
mapUserDogs[userSelected] = dogs

// 此时再调用contains就会返回true
println(mapUserDogs.contains(userSelected)) // 输出true

3. 自定义equals和hashCode,基于不变的唯一标识

给User添加一个不变的唯一标识(比如id),然后手动实现equals()和hashCode(),只依赖这个不变标识。这样修改其他属性不会影响键的匹配:

data class User(
    val id: String, // 唯一不变标识
    var name: String,
    var project: String,
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as User
        return id == other.id
    }

    override fun hashCode(): Int {
        return id.hashCode()
    }
}

使用这种方式,无论修改name还是project,equals和hashCode的结果都不会变,Map的contains方法始终能正确识别该键。


内容的提问来源于stack exchange,提问作者Daniel Florez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 02:40:34