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

Kotlin集合异常行为:可变数据类存入Set后的问题解析

Kotlin数据类在MutableSet中的行为解析

核心原因:数据类的equals与hashCode实现

Kotlin数据类会自动基于主构造器中的所有属性生成equals()和hashCode()方法:

  • equals():先判断是否为同一引用,是则直接返回true;否则逐一比较所有属性的值,全部相等才返回true。
  • hashCode():基于所有属性的值计算哈希值,只要属性变化,哈希值就会跟着改变。

Kotlin的mutableSetOf()默认实现是LinkedHashSet,底层逻辑和HashSet一致:依靠hashCode()确定元素存储的哈希桶,依靠equals()判断元素是否相等。


疑问1:修改属性后,set.contains(pos)为何有时返回true有时返回false?

当你把pos实例添加到set后,修改它的x/y属性:

  1. 实例的引用没有改变,但hashCode()会因为属性更新而变化。
  2. 调用set.contains(pos)时,流程是:
    • 先计算当前pos的新哈希值,去对应的哈希桶查找元素。
    • 如果新哈希值和添加时的旧哈希值碰巧相同(哈希碰撞):会找到存储该实例的桶,然后用equals()比较——因为是同一引用,equals()直接返回true,所以contains返回true。
    • 如果新哈希值和旧哈希值不同:会去新的哈希桶查找,而该实例实际存在旧哈希桶里,新桶找不到,所以contains返回false。

你代码中修改y为-37时刚好触发了哈希碰撞,所以返回true;改成-35时哈希值不同,就返回false。


疑问2:为何setByCopy.contains(pos)返回false?

setByCopy里存的是pos.copy()生成的全新实例:

  • 这个新实例的属性是添加时的x=-9,y=-6,但当前pos的属性已经变成x=-8,y=-37。
  • 调用contains()时,哪怕当前pos的哈希值和copy实例的哈希值碰巧一致,equals()会比较两者的属性值,结果不相等,所以返回false。

总结:可变属性数据类存入Set的风险

因为数据类的hashCode和equals依赖可变属性,一旦属性修改:

  • 实例在Set中的存储位置(哈希桶)和当前哈希值不匹配,会导致contains()、remove()等方法行为异常。
  • 如果要维护唯一坐标的Set,建议把数据类的属性改成不可变的val,每次更新坐标时创建新实例,这样Set的行为会完全符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 15:50:15