What's a good recipe for overriding hashcode in Dart?

DartHashcode

Dart Problem Overview


I find myself wanting to override hashcode and == for an object, and I'm wondering if there are best practices for how to implement a hashcode that depends on multiple attributes, and it seems like there are some Dart-specific considerations.

The simplest answer would be to XOR the hashes of all the attributes together, and it's probably not too bad. There's also an example in Dart Up and Running at 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;
 }

but that seems like it expects truncating integer semantics and in Dart overflowing the range of JS integers seems bad for hashing.

We could also do that and just truncate to 32 bits after each operation.

For my application the expected size of the set is very small and almost anything would do, but I'm surprised not to see a standard recipe for the general case. Does anyone have any experience or strong experience with this?

Dart Solutions


Solution 1 - Dart

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.

Solution 2 - Dart

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

Solution 3 - Dart

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

Solution 4 - Dart

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];
}

Solution 5 - Dart

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.

Solution 6 - Dart

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlan KnightView Question on Stackoverflow
Solution 1 - DartPatrice ChalinView Answer on Stackoverflow
Solution 2 - DartDaniel BrotherstonView Answer on Stackoverflow
Solution 3 - DartPavelView Answer on Stackoverflow
Solution 4 - DartJoander Vieira CândidoView Answer on Stackoverflow
Solution 5 - DartmanikantaView Answer on Stackoverflow
Solution 6 - DartSteven RooseView Answer on Stackoverflow