J F
J F

Reputation: 1128

Dart: Strange behavior with class generated hashCode

I'm having a strange behavior with a new class in which I had to override == operator and hashCode method.

I'll make you an example. Let's say tha we have a Test class that is made like the following:

import 'package:quiver/core.dart';
import 'package:collection/collection.dart';

class Test {

  List testList = [];

  operator ==(Object other) {
    if (other is! Test) return false;
    Function deepEq = const DeepCollectionEquality.unordered().equals;
    return deepEq(testList, (other as Test).testList);
  }

  int get hashCode => hashObjects(testList);
}

Now I run the following code:

main() {
  Test test = new Test();
  //test.testList.add([]);
  print('Test,  hash: ${test.hashCode}');
  Test test_2 = new Test();
  //test_2.testList.add([]);
  print('Test_2,  hash: ${test_2.hashCode}');
  print("is ${test == test_2} that Test and Test2 are equal");
  Function deepEq = const DeepCollectionEquality.unordered().equals;
  bool b = deepEq(test, test_2);
  print("is $b that Test and Test2 are deep equal");

  List l1 = [test];
  print('L1 hash: ${l1.hashCode}');
  List l2 = [test_2];
  print('L2 hash: ${l2.hashCode}');
  deepEq = const DeepCollectionEquality.unordered().equals;
  b = deepEq(l1, l2);
  print("is $b that List1 and List2 are deep equal");
}

The above code print the following, that is what I had expected:

Test,  hash: 0 
Test_2,  hash: 0 
is true that Test and Test2 are equal
is true that Test and Test2 are deep equal 
L1 hash: 89819481 
L2 hash: 414841104 
is true that List1 and List2 are deep equal

Now If I uncomment the lines:

  test.testList.add([]);
  test_2.testList.add([]);

and rerun the program, I have the following result:

Test,  hash: 76603616
Test_2,  hash: 386421917
is true that Test and Test2 are equal
is true that Test and Test2 are deep equal
L1 hash: 915458568
L2 hash: 503799923
is false that List1 and List2 are deep equal

This is not what I expected. Given that DeepCollectionEquality internally uses hashCode to check equality, I can understand that hashObjects when encounters a List as component of the main List uses the List hashCode, instead of generating a new one reading all the components. What I do not understand is why Test and Test 2 are considred equals while List1 and List2 are not. It depends on how many levels of inner Lists are present in the Iterable for which I'm creating the hash code? HashObjects works like that by design or this should be considered a bug? Is there something wrong in how I use hashObjects?

Upvotes: 1

Views: 1757

Answers (1)

Reimer Behrends
Reimer Behrends

Reputation: 8740

Your problem is that hashObjects() does not recurse and two different instances of the empty list will typically have different hash values (unless both are const or by happenstance).

Thus, hashObjects() works as you expect it for flat lists, but not for lists of lists, unless the respective sublists are identical objects (and not just structurally equal).

The following example should illustrates this:

import 'package:quiver/core.dart';

void main() {
  var a = [];
  var b = [];
  var c = [];
  print(a.hashCode); // different
  print(b.hashCode);
  print(hashObjects(a)); // both same, both zero
  print(hashObjects(b));
  print(hashObjects(a..add(1))); // both same
  print(hashObjects(b..add(1)));
  print(hashObjects(a..add(c))); // both same, because `c` is identical
  print(hashObjects(b..add(c)));
  print(hashObjects(a..add([]))); // different, because the two empty
  print(hashObjects(b..add([]))); // lists are not identical.
}

Upvotes: 1

Related Questions