Alan Knight
Alan Knight

Reputation: 2971

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

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?

Upvotes: 110

Views: 47379

Answers (6)

Patrice Chalin
Patrice Chalin

Reputation: 16100

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);

  @override
  bool operator ==(o) => o is Person && name == o.name && age == o.age;

  @override
  int get hashCode => hash2(name.hashCode, age.hashCode);
}

Also see this post for a slightly lengthier discussion.

Upvotes: 79

manikanta
manikanta

Reputation: 8500

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.

Upvotes: 94

Daniel Brotherston
Daniel Brotherston

Reputation: 2041

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

Upvotes: 44

Pavel
Pavel

Reputation: 5876

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

Upvotes: 19

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

Upvotes: 12

Steven Roose
Steven Roose

Reputation: 2769

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.

Upvotes: -2

Related Questions