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

Kotlin中Java HashMap无法通过存在的Key对象删除条目问题排查

HashMap中使用Data Class作为Key删除失败的排查与解决

问题场景

用Waypoint(继承自抽象类Markup的data class)作为HashMap<Markup, Feature>的Key,预期传入的entry是Map中存在的有效Key,但偶尔会出现remove(entry)返回null的情况。即使通过uuid从Map中找到对应的Key,断言该Key与传入的entryequals和引用都相等,调用containsKey(markup)依然会失败,改用mutableScatterMapOf()也存在同样问题。

相关代码

集合操作代码

val markupFeatureCollection = HashMap<Markup, Feature>()

fun remove(entry: Markup) {
    // 预期entry是Map中存在的有效Key
    val feature = markupFeatureCollection.remove(entry)

    // 出现异常情况:删除失败,但需要保留Feature以便后续复用
    if (feature == null) {
        // 通过uuid查找Map中的对应条目
        val member = markupFeatureCollection.filter { it.key.uuid == entry.uuid }

        // 断言存在对应Feature
        member.values.first()

        // 获取找到的Key
        val markup = member.keys.first()

        // 验证找到的Key与传入entry的相等性
        assert(markup == entry) {
            "entry key was found in markupFeatureCollection but could not be removed"
        }

        assert(markup === entry) {
            "entry key was found in markupFeatureCollection but could not be removed"
        }

        // 打印对比两个对象
        println("xxx: $markup")
        println("xxx: $entry")

        // 此处断言始终失败:明明是从Map中取出的Key,containsKey却返回false
        assert(markupFeatureCollection.containsKey(markup))
    }
}

类定义

abstract class Markup(
    open val uuid: String,
    open val name: String,
    open val geoJson: JsonObject
)

data class Waypoint(
    override val uuid: String = UUID.randomUUID().toString(),
    override val name: String = createDefaultName(),
    override val geoJson: JsonObject
):Markup(
    uuid = uuid,
    name = name,
    geoJson = geoJson,
)

根本原因

虽然Waypoint是data class,且所有成员都声明为val,但其中的geoJson是可变的JsonObject。Data class的hashCode()默认由所有成员变量计算生成,当geoJson的内容被修改后,Waypoint的hashCode会发生变化。

HashMap的工作依赖于Key的hashCode定位桶位置,当Key存入Map后如果hashCode改变,后续的查找、删除操作会根据新的hashCode去错误的桶中查找,即使Key的引用完全相同,也无法匹配到原来的条目,导致containsKey和remove操作失败。

解决方案

改用不可变的uuid作为HashMap的Key,替换原来的Markup对象。因为uuid是字符串类型,内容不可变,hashCode始终稳定,不会出现因内部状态变化导致的匹配失败问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 12:06:17