Flutter 中的相等判断研究总结

一、需求来源

最近研究 Flutter 中重载相等运算符,使用场景是模型判断,集合模型、模型数组的判断处理等;

1、identical 函数

目前在 /bin/cache/pkg/sky_engine/lib/core/identical.dart

中定义了 identical 用于检查两个对象引用是否指向同一对象。官方示例代码很清新的帮我们理解 identical 函数的使用。

css 复制代码
external bool identical 用于对象(Object? a, Object? b);

官方示例代码:

dart 复制代码
var o = new Object();
var isIdentical = identical(o, new Object()); // false, different objects.
isIdentical = identical(o, o); // true, same object.
isIdentical = identical(const Object(), const Object()); // true, const canonicalizes.
isIdentical = identical([1], [1]); // false, different new objects.
isIdentical = identical(const [1], const [1]); // true.
isIdentical = identical(const [1], const [2]); // false.
isIdentical = identical(2, 1 + 1); // true, integers canonicalize.

///
var pair = (1, "a"); // Create a record.
isIdentical = identical(pair, pair); // true or false, can be either.

///
var pair2 = (1, "a"); // Create another(?) record.
isIdentical = identical(pair, pair2); // true or false, can be either.

///
isIdentical = identical(pair, (2, "a")); // false, not identical values.
isIdentical = identical(pair, (1, "a", more: true)); // false, wrong shape.

2、集合类型比较

目前在 collections.dart /packages/flutter/lib/src/foundation/collections.dart 中提供了以下方法:

集合类型比较 复制代码
// set 比较
bool setEquals<T>(Set<T>? a, Set<T>? b)

// list 比较
bool listEquals<T>(List<T>? a, List<T>? b)

// map 比较
bool mapEquals<T, U>(Map<T, U>? a, Map<T, U>? b)

可以进行三种常用集合的相等比较;

bash 复制代码
final list1 = ["aaa"];
final list2 = ["aaa"];
debugPrint("${DateTime.now()} list ==: ${list1 == list2}");
debugPrint("${DateTime.now()} list listEquals: ${listEquals(list1, list2)}");
//flutter: 2023-11-12 15:58:10.388805 list ==: false
//flutter: 2023-11-12 15:58:10.389081 list listEquals: true


final map1 = {"a": "aa"};
final map2 = {"a": "aa"};
debugPrint("${DateTime.now()} map ==: ${map1 == map2}");
debugPrint("${DateTime.now()} map mapEquals: ${mapEquals(map1, map2)}");
//flutter: 2023-11-12 15:58:10.389159 map ==: false
//flutter: 2023-11-12 15:58:10.389209 map mapEquals: true


final set1 = {'a'};
final set2 = {'a'};
debugPrint("${DateTime.now()} set ==: ${set1 == set2}");
debugPrint("${DateTime.now()} set setEquals: ${setEquals(set1, set2)}");
//flutter: 2023-11-12 15:58:10.389426 set ==: false
//flutter: 2023-11-12 15:58:10.389504 set setEquals: true

2.1 源码

listEquals 实现源码:

dart 复制代码
bool listEquals<T>(List<T>? a, List<T>? b) {
  if (a == null) {
    return b == null;
  }
  if (b == null || a.length != b.length) {
    return false;
  }
  if (identical(a, b)) {
    return true;
  }
  for (int index = 0; index < a.length; index += 1) {
    if (a[index] != b[index]) {
      return false;
    }
  }
  return true;
}

mapEquals 实现源码:

dart 复制代码
bool mapEquals<T, U>(Map<T, U>? a, Map<T, U>? b) {
  if (a == null) {
    return b == null;
  }
  if (b == null || a.length != b.length) {
    return false;
  }
  if (identical(a, b)) {
    return true;
  }
  for (final T key in a.keys) {
    if (!b.containsKey(key) || b[key] != a[key]) {
      return false;
    }
  }
  return true;
}

setEquals 实现源码:

dart 复制代码
bool setEquals<T>(Set<T>? a, Set<T>? b) {
  if (a == null) {
    return b == null;
  }
  if (b == null || a.length != b.length) {
    return false;
  }
  if (identical(a, b)) {
    return true;
  }
  for (final T value in a) {
    if (!b.contains(value)) {
      return false;
    }
  }
  return true;
}

实现都很简单,也都是基于 identical 函数,如果你还没理解 identical 使用,可以翻到上面或者sdk 源码再看一遍。

二、模型的判等实现

dart 复制代码
class NPerson {
  NPerson({
    required this.name,
    required this.age,
  });

  String? name;
  int? age;

  NPerson.fromJson(Map<String, dynamic> json) {
    name = json['name'];
    age = json['age'];
  }

  Map<String, dynamic> toJson() {
    final data = Map<String, dynamic>();
    data['name'] = name;
    data['age'] = age;
    return data;
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }

    final isEqual = other is NPerson &&
        runtimeType == other.runtimeType &&
        mapEquals(toJson(), other.toJson());
    return isEqual;
  }

  @override
  int get hashCode => name.hashCode ^ age.hashCode;

  // hashCode 实现2 和上边选其一即可
  int get hashCode1 => Object.hash(
      name.hashCode,
      age.hashCode,
  );
}

目前有网上找到的 hashCode 实现有两种:

1)、基于属性值的 hashCode ^ 得出最终哈希值;

2)、基于所有属性值的 Object.hash 得出最终哈希值;

四、总结

1、hashCode ^ 得出最终哈希值会比 Object.hash 得出最终哈希值大出很多,目前不清楚再多属性模型(100 个以上)时,计算是否会影响性能;
2、equatable 是点赞较多第三方库,大家根据自身项目情况选择使用;
相关推荐
上单带刀不带妹3 分钟前
手写 Vue 中虚拟 DOM 到真实 DOM 的完整过程
开发语言·前端·javascript·vue.js·前端框架
杨进军24 分钟前
React 创建根节点 createRoot
前端·react.js·前端框架
ModyQyW39 分钟前
用 AI 驱动 wot-design-uni 开发小程序
前端·uni-app
说码解字1 小时前
Kotlin lazy 委托的底层实现原理
前端
爱分享的程序员1 小时前
前端面试专栏-算法篇:18. 查找算法(二分查找、哈希查找)
前端·javascript·node.js
翻滚吧键盘1 小时前
vue 条件渲染(v-if v-else-if v-else v-show)
前端·javascript·vue.js
vim怎么退出2 小时前
万字长文带你了解微前端架构
前端·微服务·前端框架
你这个年龄怎么睡得着的2 小时前
为什么 JavaScript 中 'str' 不是对象,却能调用方法?
前端·javascript·面试
Java水解2 小时前
前端常用单位em/px/rem/vh/vm到底有什么区别?
前端
CAD老兵2 小时前
Vite 如何借助 esbuild 实现极速 Dev Server 体验,并支持无 source map 的源码调试
前端