【问题标题】:Dart: Strange behavior with class generated hashCodeDart:类生成的 hashCode 的奇怪行为
【发布时间】:2017-09-14 14:18:05
【问题描述】:

我在一个新类中遇到了一个奇怪的行为,我必须在其中重写 == 运算符和 hashCode 方法。

我会给你举个例子。 假设我们有一个如下所示的 Test 类:

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

现在我运行以下代码:

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

上面的代码打印出以下内容,这正是我所期望的:

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

现在如果我取消注释这些行:

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

重新运行程序,结果如下:

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

这不是我所期望的。鉴于 DeepCollectionEquality 内部使用 hashCode 来检查相等性,我可以理解 hashObjects 在遇到作为主 List 的组件的 List 时使用 List hashCode,而不是生成一个读取所有组件的新哈希码。我不明白为什么 Test 和 Test 2 被认为是相等的,而 List1 和 List2 不是。 这取决于我为其创建哈希码的 Iterable 中存在多少级别的内部列表? HashObjects 是这样设计的,或者这应该被认为是一个错误? 我使用 hashObjects 的方式有问题吗?

【问题讨论】:

    标签: dart hashcode


    【解决方案1】:

    您的问题是 hashObjects() 不会递归,并且空列表的两个不同实例通常具有不同的哈希值(除非两者都是 const 或偶然)。

    因此,hashObjects() 对平面列表起作用,但对列表列表不起作用,除非各个子列表是相同的对象(而不仅仅是在结构上相同)。

    以下示例应说明这一点:

    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.
    }
    

    【讨论】:

    • 谢谢 Reimer,我应该认为这是一个错误,还是按设计就这样工作?
    • 它没有明确记录,但我怀疑它是设计使然,因为对于任意数据结构遍历数据的正确方法并不总是很清楚(想想你可能想要的缓存条目跳过,例如),哈希码库是由相当基本的构建块组成的。
    • 在 quiver 文档(即here)中明确喊出有帮助吗?类似于:“请注意,hashObjects 不会递归遍历可迭代对象。”
    • 你正在使用const DeepCollectionEquality.unordered() 来表示相等,所以你也应该使用它来进行哈希:int get hashCode => const DeepCollectionEquality.unordered().hash(testList)。相等和哈希码应该一致,因此当使用任何Equality 类进行相等时,您应该始终使用其hash 函数作为哈希码。
    猜你喜欢
    • 1970-01-01
    • 2019-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 2019-12-11
    • 1970-01-01
    相关资源
    最近更新 更多