一、需求来源
最近研究 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 得出最终哈希值;