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 是点赞较多第三方库,大家根据自身项目情况选择使用;
相关推荐
sunbyte3 小时前
Tailwind CSS 初学者入门指南:项目集成,主要变更内容!
前端·css
可爱的秋秋啊3 小时前
vue3,element ui框架中为el-table表格实现自动滚动,并实现表头汇总数据
前端·vue.js·笔记·elementui
一夜枫林4 小时前
uniapp自定义拖拽排列
前端·javascript·uni-app
IT瘾君5 小时前
JavaWeb:Html&Css
前端·html
264玫瑰资源库6 小时前
问道数码兽 怀旧剧情回合手游源码搭建教程(反查重优化版)
java·开发语言·前端·游戏
喝拿铁写前端6 小时前
从圣经Babel到现代编译器:没开玩笑,普通程序员也能写出自己的编译器!
前端·架构·前端框架
HED6 小时前
VUE项目发版后用户访问的仍然是旧页面?原因和解决方案都在这啦!
前端·vue.js
拉不动的猪7 小时前
前端自做埋点,我们应该要注意的几个问题
前端·javascript·面试
王景程7 小时前
如何测试短信接口
java·服务器·前端
安冬的码畜日常7 小时前
【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)
开发语言·前端·人工智能·ai·扫雷游戏·ai辅助编程·辅助编程