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 是点赞较多第三方库,大家根据自身项目情况选择使用;
相关推荐
Filotimo_12 分钟前
2.CSS3.(3).html
前端·css3
whyfail15 分钟前
React v19.2版本
前端·javascript·react.js
慧慧吖@22 分钟前
react基础
前端·javascript·react.js
浪裡遊32 分钟前
MUI组件库与主题系统全面指南
开发语言·前端·javascript·vue.js·react.js·前端框架·node.js
DiXinWang1 小时前
关闭谷歌浏览器提示“若要接收后续 Google Chrome 更新,您需使用 Windows 10 或更高版本”的方法
前端·chrome
CoderYanger1 小时前
前端基础——HTML练习项目:填写简历信息
前端·css·职场和发展·html
muyouking111 小时前
深入理解 HTML `<label>` 的 `for` 属性:提升表单可访问性与用户体验
前端·html·ux
IT_陈寒1 小时前
Java性能调优:从GC日志分析到实战优化的5个关键技巧,让你的应用快如闪电!
前端·人工智能·后端
Mintopia2 小时前
🚀 Next.js API 压力测试:一场前端与后端的“极限拉扯”
前端·后端·全栈
Mintopia2 小时前
🛡️ 对抗性攻击与防御:WebAI模型的安全加固技术
前端·javascript·aigc