Menu
CoddyTech

重写 hashCode 和 ==

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 71 课,共 110 课。

默认情况下,Dart 中的两个对象只有在内存中是同一个实例时才相等。这意味着,即使两个对象的值完全相同,它们仍会被视为不同的对象:

class Point {
  int x, y;
  Point(this.x, this.y);
}

void main() {
  var p1 = Point(3, 4);
  var p2 = Point(3, 4);
  print(p1 == p2);  // false,因为它们是不同的实例!
}

要按值比较对象,你需要同时重写 == 运算符和 hashCode。这两者必须始终一起重写:如果两个对象相等,它们的哈希码就必须相同:

class Point {
  final int x, y;
  Point(this.x, this.y);
  
  @override
  bool operator ==(Object other) {
    if (other is! Point) return false;
    return x == other.x && y == other.y;
  }
  
  @override
  int get hashCode => Object.hash(x, y);
}

void main() {
  var p1 = Point(3, 4);
  var p2 = Point(3, 4);
  print(p1 == p2);  // true,值相同!
}

hashCode 对于 Set 和 Map 键等集合至关重要。Dart 的 Object.hash() 辅助函数会将多个值合并为一个哈希码。如果没有正确的 hashCode,即使重写了 ==,你的对象在基于哈希的集合中也无法正常工作。

challenge icon

挑战

简单

我们来构建一个坐标系,让点可以按值而不是内存地址进行比较!你将创建一个 Coordinate 类,正确实现基于值的相等性,让相等性检查和基于哈希的集合(如 Set)都能正常工作。

你将把代码分成两个文件:

  • coordinate.dart:创建一个 Coordinate 类,用来表示二维空间中的一个点,并包含 final int x 和 final int y 字段。重写 == 运算符和 hashCode,这样两个 x 和 y 值相同的坐标就会被视为相等。使用 Object.hash() 根据这两个字段生成哈希码。
  • main.dart:导入坐标文件,并演示基于值的相等性如何改变对象比较行为:
    • 创建两个不同的 Coordinate 对象,它们的 x 都是 5,y 都是 10
    • 使用 == 打印它们是否相等
    • 使用 identical() 打印它们是否完全相同(是同一个实例)
    • 创建一个 Set<Coordinate>,并将两个坐标都添加到其中
    • 打印集合的长度(由于它们相等,长度应该是 1!)
    • 创建第三个坐标,x 为 3,y 为 7,并将它添加到集合中
    • 打印集合的最终长度

这个挑战说明了为什么必须始终一起重写 hashCode 和 ==。Set 使用哈希码来组织元素,因此如果没有正确的 hashCode,即使相等的对象也可能被当作重复项存储!

预期输出:

true
false
1
2

亲手试试

import 'coordinate.dart';

void main() {
  // TODO: 创建两个不同的 Coordinate 对象,两个对象的 x 都是 5,y 都是 10

  // TODO: 用 == 输出它们是否相等

  // TODO: 用 identical() 输出它们是否是同一个实例

  // TODO: 创建一个 Set<Coordinate>,并将这两个坐标都添加进去

  // TODO: 输出集合的长度(它们相等,所以应该是 1!)

  // TODO: 创建第三个坐标,x 为 3,y 为 7,并将它添加到集合中

  // TODO: 输出集合最终的长度
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Dart 在线编译器