Dart中重写hashCode getter后Set出现异常行为的原因
问题原因分析
先看对应的代码实现:
class Foo { double field = 0; @override bool operator ==(Object other) { if (other is! Foo) return false; return field == other.field; } @override int get hashCode => field.hashCode; } void main() { final set = Set<Foo>(); final variable = Foo(); set.add(variable); // add variable to Set variable.field = 1; // change variable for (var v in set) print(v == variable); // true: content of the Set changed as expected print(set.contains(variable)); // false: I would expect true here }
Dart里的Set是基于哈希表实现的,它的查找和存储逻辑完全依赖hashCode和==两个方法,这就是问题的核心:
- 添加元素时:调用
set.add(variable)时,Set会用当时variable的hashCode(此时field=0,对应哈希值是0的哈希码)计算存储位置,把元素存在这个位置里。 - 修改元素后:你把
variable.field改成1,由于Foo的hashCode直接绑定field的哈希值,所以这个对象的哈希码也跟着变成了1的哈希值。 contains的查找逻辑:调用set.contains(variable)时,Set会先用当前的新哈希码去哈希表中找对应的位置,但这个位置和当初存储元素的旧位置完全不一样,自然找不到目标元素,直接返回false。- 遍历比较的逻辑:而遍历Set时,是直接取出哈希表里所有元素,逐个和
variable用==方法对比——此时两个对象的field都是1,==方法返回true,所以打印结果为true。
总结一下:Set找元素是先靠哈希码定位位置,再用==验证;你修改对象导致哈希码变更,Set找不到原来的存储位置,所以contains返回false,但遍历是全量逐个比较,所以能匹配成功。
内容的提问来源于stack exchange,提问作者Andrey
相关产品推荐
相关产品推荐

