在 Dart 中重写 hashcode 的好方法是什么?

我发现自己想要为一个对象覆盖 hashcode 和 = = ,我想知道是否存在实现依赖于多个属性的 hashcode 的最佳实践,而且似乎存在一些特定于 Dart 的注意事项。

最简单的答案是将所有属性的哈希值异或放在一起,这可能也不是太糟糕。还有一个例子在飞驰和运行在 https://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html

  // Override hashCode using strategy from Effective Java, Chapter 11.
int get hashCode {
int result = 17;
result = 37 * result + firstName.hashCode;
result = 37 * result + lastName.hashCode;
return result;
}

但是它似乎期望截断整数语义,并且在 Dart 中,JS 整数的范围过大似乎不利于散列。

我们也可以这样做,每次操作后只截断到32位。

对于我的应用程序,预期的集合大小非常小,几乎任何东西都可以,但是我很惊讶没有看到通用情况的标准配方。有人有这方面的经验或丰富经验吗?

32536 次浏览

Since Dart is so similar to Java, you can surely find good references on hashCodes for Java that are applicable for Dart too.

A little googling took me to the Wikipedia page on Java's Object.hashCode(). Has a very basic example for the hashcode of a simple object. A popular methodology is to perform a multiplication with a prime number (different ones) and adding some value for each property of the object.

This question f.e. explains why the number 31 is chosen for multiplication for the String.hashCode() method.

More detailed examples of hashcode implementations can be easily found using Google.

The quiver package provides helper functions hash2, hash3, etc., which simplify the task of implementing hashCode, with some assurance that it works properly under the Dart VM and when compiled to JavaScript.

import 'package:quiver/core.dart';


class Person {
String name;
int age;


Person(this.name, this.age);


bool operator ==(o) => o is Person && name == o.name && age == o.age;
int get hashCode => hash2(name.hashCode, age.hashCode);
}

Also see this post for a slightly lengthier discussion.

The equatable package can help

import 'package:equatable/equatable.dart';


class Person extends Equatable {
final String name;
final int age;


Person(this.name, this.age);


@override
List<Object> get props => [name, age];
}

Now Person will use == and hashCode from Equatable, which takes props list that you give

I recomend "equatable" plugin

https://pub.dev/packages/equatable

Example:

Raw mode:

class Person {
final String name;


const Person(this.name);


@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;


@override
int get hashCode => name.hashCode;
}

With equatable :

import 'package:equatable/equatable.dart';


class Person extends Equatable {
final String name;


Person(this.name);


@override
List<Object> get props => [name];
}

In the interests of minimizing dependencies, if you are already depending on flutter, but not depending on something like quiver, the dart:ui library contains utilites, hashValues and hashList for creating and combining hash values. If combining list values, one must be careful to ensure the equality operator and hashcode match behaviour. If the hash code computes it's hash deeply, then use deep equality, else, use shallow equality.

class Example {
final String value1;
final Object value2;
final List<Object> deep;
final List<Object> shallow;


Example({this.value1, this.value2, this.deep, this.shallow});


@override
operator ==(o) =>
o is Example &&
o.value1 == value1 &&
o.value2 == value2 &&
listEquals(o.deep, deep) &&
o.shallow == shallow;


@override
int get hashCode => hashValues(value1, value2, hashList(deep), shallow);
}

Flutter API docs for hashValues

Flutter API docs for hashList

Since version 2.14, Dart language added support for Object.hash(), along with Object.hashAll() and Object.hashAllUnordered()

hash() doc:

Creates a combined hash code for a number of objects.

Example:

class SomeObject {
final Object a, b, c;
SomeObject(this.a, this.b, this.c);


bool operator==(Object other) =>
other is SomeObject && a == other.a && b == other.b && c == other.c;


int get hashCode => Object.hash(a, b, c); // <----- here
}

Note from doc on implementation:

The hash value generated by this function is not guranteed to be stable over different runs of the same program, or between code run in different isolates of the same program. The exact algorithm used may differ between different platforms, or between different versions of the platform libraries, and it may depend on values that change on each program execution.