今天在开发的过程中,有一个需求:将一些对象数据存储在变量中,相同数据进行去重。
我首先想到的是dart中的内置数据类型Set, Set是一个无序且元素唯一的集合。
原本我是想用List的,但是去重我还需要遍历它来处理,比较麻烦。而使用Set就方便得多,最终还可以调用toList方法转成List。
于是,我通过字面量的方式创建Set集合,然后将t1、t2、t3对象add进data里。
class TestObject {
final String? a;
final String? b;
const TestObject({this.a, this.b});
}
void main() {
var t1 = TestObject(a: 'a', b: 'b');
var t2 = TestObject(a: 'a', b: 'b');
var t3 = TestObject(a: 'a', b: 'c');
Set<TestObject> data = {};
data.add(t1);
data.add(t2);
data.add(t3);
}
我原以为t1应该和t2是相等的,会被过滤掉,data中只剩下t2和t3,但结果却出乎意料,三个对象都在data中,t1和t2也并不相等。
接着,我将t1、t2、t3改为常量对象,因为创建它们的类是一个常量构造函数,我可以使用const修饰符设置常量对象。
class TestObject {
final String? a;
final String? b;
const TestObject({this.a, this.b});
}
void main() {
var t1 = const TestObject(a: 'a', b: 'b');
var t2 = const TestObject(a: 'a', b: 'b');
var t3 = const TestObject(a: 'a', b: 'c');
Set<TestObject> data = {};
data.add(t1);
data.add(t2);
data.add(t3);
}
这一次,data中只剩下t1和t3,t2被认为跟t1重复,并没有添加进来。
我这才意识到,Set并不能简单的对对象进行去重处理,而是要认为对象相等的情况下才能进行数据去重。那对象之间是怎么判定是否相等的呢?以下是我通过研究,总结出的一些判定要点:
1、同一个类创建的实例对象才有比较的价值
通常情况下,每一个实例对象都自带一个随机的hashCode属性,当比较两个对象是否相等时,实际上就是对比hashCode的值是否相等。而由不同类创建出来的实例对象,hashCode的值永远都是不相同的,所以它们无法进行比较。这里所说的比较就是单纯的说 a == b,a跟b同时引用了一个内存地址。
2、同一个类创建出的常量实例对象,只要它们的属性和值一致,它们就是相等的
不知道你是否明白什么是常量构造函数和常量实例对象?首先,常量构造函数需要以const关键字修饰。其次,它的成员属性都必须由final关键字修饰。最后,实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例。
通过同一个类创建出来的常量实例对象,属性和值一样,hashCode的值是一致的,也就是说它们所指向的内存地址一致。所以通过 == 比较,它们是相等的。
void main() {
var t1 = const TestObject(a: 'a', b: 'b');
var t2 = const TestObject(a: 'a', b: 'b');
var t3 = const TestObject(a: 'a', b: 'c');
print('${t1 == t2}'); // true
print('${identical(t1, t2)}'); // true
print('${t1 == t3}'); // false
print('${identical(t1, t3)}'); // false
}
tips:
1、在Flutter中使用const修饰的组件可以减少内存开销,每次重新构造时,都不会重新构建const组件。
2、identical方法用于检查两个引用是否指向同一个对象(指向同一个内存地址)
3、同一个类创建出的非常量实例对象,可能通过重载操作符“==”和hashCode方法让它们相等。
在dart中可能通过operator关键字对==进行重载,同时也要将hashCode一同重载。这样我们就可以自己编写对象相等逻辑,让原本不相等的两个对象在某些条件下认为是相等的。
class TestObject {
String? a;
String? b;
TestObject({this.a, this.b});
/// 通过operator关键字重写==方法
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is TestObject && a == other.a && b == other.b;
}
/// 重写hashCode方法
@override
int get hashCode => a.hashCode + b.hashCode;
}
void main() {
var t1 = TestObject(a: 'a', b: 'b');
var t2 = TestObject(a: 'a', b: 'b');
var t3 = TestObject(a: 'a', b: 'c');
print('${t1 == t2}'); // true
print('${identical(t1, t2)}'); // false
print('${t1 == t3}'); // false
print('${identical(t1, t3)}'); // false
}