为何Julia中添加mutable关键字后结构体hash值会不同?
核心原因
这是 Julia 语言对不可变结构体、可变结构体设计的默认语义差异导致的,和字段内容是否修改无关:
- 普通
struct声明的是不可变结构体,实例创建后所有字段值都不能重新绑定。这类结构默认采用值语义实现相等判断(==)和哈希计算(hash()):只要两个实例的所有字段值完全相等,就判定为同一个值,哈希结果也完全一致。无mutable标记的IntroductionMessage就属于这类,因此两个内容相同的实例哈希值一致,和字符串、数值这类基础值类型的行为完全相同。 - 加了
mutable关键字的可变结构体,实例创建后字段可以随时修改,因此默认采用身份语义实现相等判断和哈希计算:判断相等、计算哈希的依据是对象本身的内存地址/唯一身份标识,和字段存储的内容无关。哪怕两个实例的字段内容完全一模一样,只要是分别创建的两个独立对象,就会被判定为不相等,哈希结果也会存在明显差异。
这种设计是为了规避可变对象作为哈希键的逻辑风险:如果可变对象默认按字段值算哈希,当你把实例存入Set、或者作为Dict的键之后,一旦修改实例的字段值,实例的哈希就会发生变化,直接导致集合内部存储的索引失效,后续无法正常找到对应对象,引发很难排查的bug。
你可以用下面的代码直接验证两类结构的默认行为差异:
# 不可变结构:值语义 struct ImmutableMessage message::String end i1 = ImmutableMessage("Hello World") i2 = ImmutableMessage("Hello World") println(i1 == i2) # 输出 true println(hash(i1) == hash(i2)) # 输出 true # 可变结构:身份语义 mutable struct MutableMessage message::String end m1 = MutableMessage("Hello World") m2 = MutableMessage("Hello World") println(m1 == m2) # 输出 false println(hash(m1) == hash(m2)) # 输出 false
如果你需要让自定义可变结构也按值计算哈希、判断相等,手动重载对应方法即可:
# 重载相等判断 Base.:(==)(a::MutableMessage, b::MutableMessage) = a.message == b.message # 重载哈希计算,逻辑必须和==保持一致 Base.hash(x::MutableMessage, h::UInt) = hash(x.message, h)
重载完成后,两个字段内容一致的MutableMessage实例就会得到相同的哈希值了。注意如果用这类重载了值语义的可变实例作为Dict/Set的键,存入集合后不要修改实例参与哈希计算的字段,否则会破坏哈希集合的正常逻辑。
内容的提问来源于stack exchange,提问作者user19242326
相关产品推荐
相关产品推荐

