Dart开发能否以自定义对象作为Map键?为何按坐标取值返回null?
自定义类作为Map键取值返回null的问题说明
基本判定结论
自定义类可以直接作为Dart中Map的键使用,取值返回null是因为类没有实现正确的值相等判断规则。
问题根因
Dart的Map在做键匹配时,会按照固定流程校验:
- 先比对两个键的
hashCode值是否一致,哈希值不一致的对象会被直接判定为不同的键 - 哈希值一致的前提下,再通过
==运算符判断两个对象是否值相等 - 两项校验全部通过,才会认定两个对象是同一个键
原有Coordinates类没有重写上述两个方法,完全沿用了Object基类的默认实现:默认==逻辑直接比对对象的内存引用地址,默认hashCode也是基于内存地址生成。这就导致哪怕两个Coordinates实例的col、row属性值完全相同,只要是独立创建的不同实例,在Map的校验逻辑里就属于完全不同的键,最终匹配失败返回null。
修正实现
要让属性值完全一致的Coordinates实例被判定为同一个键,必须在类定义中补充重写==运算符和hashCode属性,可选两种实现方式:
原生无依赖实现
直接使用Dart内置的工具方法完成逻辑,不需要引入第三方包:
class Coordinates { final int col; final int row; Coordinates(this.col, this.row); // 重写相等判断规则 @override bool operator ==(Object? other) { return other is Coordinates && other.col == col && other.row == row; } // 重写哈希值计算规则 @override int get hashCode => Object.hash(col, row); int getCol() => col; int getRow() => row; }
其中Object.hash是Dart 2.19版本后内置的静态方法,可以直接组合多个字段的哈希值生成对象的哈希码,避免手动拼接计算的逻辑出错。
基于quiver工具包实现
如果项目已经引入quiver依赖,可以借助其提供的哈希工具简化代码:
import 'package:quiver/core.dart'; class Coordinates { final int col; final int row; Coordinates(this.col, this.row); @override bool operator ==(Object? o) => o is Coordinates && col == o.col && row == o.row; @override int get hashCode => hash2(col.hashCode, row.hashCode); int getCol() => col; int getRow() => row; }
重要注意事项:重写
==运算符时必须同步重写hashCode,只修改相等判断逻辑不修改哈希规则,依然会出现Map键匹配失败的问题——哈希值不一致的对象根本不会进入后续的相等判断环节。
内容的提问来源于stack exchange,提问作者Кадыр Атаханов
相关产品推荐
相关产品推荐

