一、集合比較
在 Dart 中,DeepCollectionEquality 類是 collection 包中的一個類,用於比較兩個集合(如 List、Set、Map 等)是否相等,並且進行深度比較。這意味着它不會比較集合的引用,而是會遞歸地比較集合中的每個元素的內容,確保集合內部的內容也完全相同。
通常,== 運算符只能比較對象的引用是否相等,而 DeepCollectionEquality 會比較集合中每個元素的值。
依賴包:
collection: ^1.18.0
import 'package:collection/collection.dart';
void main() {
// 創建兩個相同的嵌套列表
var list1 = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
];
var list2 = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
];
// 使用 DeepCollectionEquality 進行深度比較
var equality = DeepCollectionEquality();
print(equality.equals(list1, list2)); // 輸出: true
}
實際開發中的代碼:
import 'package:collection/collection.dart';
bool _compare(dynamic a, dynamic b) {
if (a is String || a is int || a is double || a is bool) {
return a == b;
} else if (a is Set || a is Map || a is List || a is Iterable) {
return const DeepCollectionEquality().equals(a, b);
}
return a == b;
}
二、用==比較對象類型
上面提到了話題是比較兩個集合是否完全一樣,比較的是兩個集合中的所有屬性,包含元素的順序;
但有時候我們也希望通過==符號就能簡單的對邊兩個對象是否相等,就像比較兩個數字或字符串一樣;
假設我們有一個類:
class Person {
final String id;
final String name;
Person({required this.id, required this.name});
}
我們需要簡單的通過==來判斷兩個Person類對象是否相等,默認情況下,比較的是引用地址:
/// p1、p2引用地址不一樣,所以不相同
Person p1 = Person(id: 1, name: '張三');
Person p2 = Person(id: 1, name: '張三');
Person p3 = p1;
print(p1 == p2); // 輸出 false
print(p1 == p3); // 引用地址相同,輸出 true
既然默認情況下,比較的是引用地址,而不是比較具體屬性,那我們需要重寫類的==符號,來改變這一規則,修改後的Person
class Person {
final int id;
final String name;
Person({required this.id, required this.name});
@override
bool operator ==(covariant Object other) {
/// identical(this, other) 用於判斷兩個對象的引用地址是否相同——默認情況
/// runtimeType == other.runtimeType 用於判斷兩個對象的類型是否相同
/// 最後,比較類的各個屬性是否相等
/// 所以:只要是引用相等,或者,屬於同一種類,並且各個屬性相等,那麼兩個都想就相等;
return identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name;
}
}
Person改成如上結構後:
前面的p1和p2相等了。
既然p1和p2相等了,那麼包含p1的集合是否contains了p2呢?答案是否定了,接着往下看。
var set = {p1};
print(set.contains(p2)); // 期望 true,但實際是 false
思考:是不是以為set.contains(p2)返回true呢?實際上返回的還是false,為什麼呢?
實際原因是:
- 集合類型(如 Set、Map 的 key)在判斷元素是否相等時,既會用 ==,也會用 hashCode。
- 只有當兩個對象的 hashCode 相等時,才會進一步用 == 判斷是否“真正相等”。
- 如果你只重寫了 ==,但沒有重寫 hashCode,那麼即使兩個對象“看起來相等”,它們的 hashCode 可能不同,導致集合類無法正確識別它們為同一個元素。
所以需要重寫hashCode:
@override
int get hashCode => id.hashCode;
最後的Person類:
class Person {
final int id;
final String name;
Person({required this.id, required this.name});
@override
bool operator ==(covariant Object other) {
/// identical(this, other) 用於判斷兩個對象的引用地址是否相同——默認情況
/// runtimeType == other.runtimeType 用於判斷兩個對象的類型是否相同
/// 最後,比較類的各個屬性是否相等
/// 所以:只要是引用相等,或者,屬於同一種類,並且各個屬性相等,那麼兩個都想就相等;
return identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name;
}
@override
int get hashCode => id.hashCode;
}