Set
Set是不能重复的集合,所以可以用Set去重;
基本数据Set去重
String、int、double类型示例,如下:
String类型:
Set s=new Set();
s.add('a');
s.add('b');
s.add('');
s.add('c');
s.add('a');
s.add('b');
print(s); // {a, b,,c}
print(s.toList()); // [a, b,,c]
int类型:
Set s=new Set();
s.add(1);
s.add(2);
s.add(1);
s.add(1);
s.add(3);
print(s); // {1, 2,3}
print(s.toList()); // [1, 2,3]
double类型:
Set s=new Set();
s.add(1.2);
s.add(1.2);
s.add(1.3);
print(s); // {1.2, 1,3}
print(s.toList()); // [1.2,1.3]
可以看到,Set集合可直接对String、int、double类型去重;
Map、List、bool类型和String、int、double类型对比示例,如下:
Set _s = Set();
_s.add({'a': 1, 'b': 2});
_s.add({'a': 1, 'b': 2});
_s.add({'a': 1, 'b': 2, 'c': 3});
_s.add([
1,
2,
]);
_s.add([
1,
2,
]);
_s.add([1, 2, 3]);
_s.add(true);
_s.add(false);
_s.add(1);
_s.add(1);
_s.add(1.2);
_s.add(1.2);
_s.add('a');
_s.add('a');
效果.png
通过上面对比示例可知,Set集合可直接对String、int、double类型去重,但对Map、List、bool类型去重失效;
自定义对象Set去重
自定义对象Set去重失效
TestModel model = TestModel('e', boolValue: false);
Set _s = Set();
_s.add(TestModel('a', boolValue: false));
_s.add(TestModel('b',));
_s.add(TestModel( 'c', ));
_s.add(TestModel( 'd',));
_s.add(TestModel('a', boolValue: true));
_s.add(TestModel('a', boolValue: true));
_s.add(model);
_s.add(model);
...
///自定义对象
class TestModel {
String title;
bool boolValue;
TestModel(this.title, {this.boolValue = true});
@override
///用于自定义对象内容展示
String toString() {
return 'class $title 值为${boolValue ?? 'null'}\n';
}
效果.png
可以看到,当自定义对象实例化为同一个对象时,Set会过滤掉同一个实例化的对象;当自定义对象实例化为不同对象时,Set不会去重,即使对象数据内容一样,这不符合我们的业务;
让自定义对象Set去重有效
试着重写自定义对象==方法:
TestModel model = TestModel('e', boolValue: false);
Set _s = Set();
_s.add(TestModel('a', boolValue: false));
_s.add(TestModel('b',));
_s.add(TestModel( 'c', ));
_s.add(TestModel( 'd',));
_s.add(TestModel('a', boolValue: true));
_s.add(TestModel('a', boolValue: true));
_s.add(model);
_s.add(model);
...
///自定义对象
class TestModel {
String title;
bool boolValue;
TestModel(this.title, {this.boolValue = true});
@override
///用于自定义对象内容展示
String toString() {
return 'class $title 值为${boolValue ?? 'null'}\n';
}
@override
///重写==方法
bool operator ==(other) {
if (null == other || other is! TestModel) {
return false;
}
final TestModel otherModel = other;
return (null != title &&
title.length > 0 &&
title.compareTo(otherModel?.title ?? '') == 0);
}
}
效果.png
可以看到,重写自定义对象==方法,Set还是不能去重;
再试下重写自定义对象hashCode和==方法
TestModel model = TestModel('e', boolValue: false);
Set _s = Set();
_s.add(TestModel('a', boolValue: false));
_s.add(TestModel('b',));
_s.add(TestModel( 'c', ));
_s.add(TestModel( 'd',));
_s.add(TestModel('a', boolValue: true));
_s.add(TestModel('a', boolValue: true));
_s.add(model);
_s.add(model);
...
///自定义对象
class TestModel {
String title;
bool boolValue;
TestModel(this.title, {this.boolValue = true});
@override
///用于自定义对象内容展示
String toString() {
return 'class $title 值为${boolValue ?? 'null'}\n';
}
@override
///重写hashCode方法
int get hashCode {
int code = title?.hashCode ?? 0;
return code;
}
@override
///重写==方法
bool operator ==(other) {
if (null == other || other is! TestModel) {
return false;
}
final TestModel otherModel = other;
return (null != title &&
title.length > 0 &&
title.compareTo(otherModel?.title ?? '') == 0);
}
}
效果.png
上面重写自定义对象hashCode和==方法,以title不同区分不同对象;
可以看到,重写自定义对象hashCode和==方法,Set集合对自定义对象去重才有效;并且Set集合里有相同对象时(相同对象为上面自定义对象title值相同的对象),后面相同的对象就不会被加入Set集合里了( 如上面title一样,boolValue不一样,这样被认为相同的对象,因为重写自定义对象hashCode和==方法,以title不同区分不同对象;Set集合里最开始加入的TestModel('a', boolValue: false),数据不会被相同对象TestModel('a', boolValue: true)覆盖 )。
Set去重
- 对于String、int、double类型,Set集合可直接去重;对于List、Map、bool类型,Set集合去重失效;
- 对于自定义对象,需要重写自定义对象hashCode和==方法,才可用Set集合去重;
- 重写自定义对象hashCode和==方法,可根据自己的业务需求决定以什么数据内容来区分不同的对象;
- 对于同一个实例化的对象,不管是基本数据对象还是自定义对象,Set集合都会去重,这种在flutter生命周期中有应用,如Form源码里;
Set集合子元素的有序性
网上介绍flutter Set集合时一般会介绍Set没有顺序,这点不太理解,可能是不能通过索引来获取对应的值吧,像Java语言的Set集合确实是无序的,但flutter的Set集合保持着子元素的有序性。 如下:
Set s=new Set();
s.add(1);
s.add(2);
s.add(1);
s.add(1);
s.add(3);
print(s.toList()); // [1, 2,3]
可以看到,将Set通过toList()方法转为List后,List里子元素的顺序和子元素插入Set集合的顺序是一致;对于自定义对象亦是如此,可自行验证。
为什么Set集合会保持子元素插入顺序呢
我们看下Set源码,如下:
/*...
* * A [HashSet] is unordered, which means that its iteration order is
* unspecified,
* * [LinkedHashSet] iterates in the insertion order of its elements, and
* * a sorted set like [SplayTreeSet] iterates the elements in sorted order.
*...
*/
abstract class Set<E> extends EfficientLengthIterable<E> {
/**
* Creates an empty [Set].
*
* The created [Set] is a plain [LinkedHashSet].
* As such, it considers elements that are equal (using [operator ==]) to be
* indistinguishable, and requires them to have a compatible
* [Object.hashCode] implementation.
*
* The set is equivalent to one created by `new LinkedHashSet<E>()`.
*/
factory Set() = LinkedHashSet<E>;
...
从源码我们可知,Set()是一个工厂构造方法,根据工厂构造方法的特点,Set是由LinkedHashSet实例化的。
A [HashSet] is unordered, which means that its iteration order is unspecified,
[LinkedHashSet] iterates in the insertion order of its elements, and a sorted set like [SplayTreeSet] iterates the elements in sorted order.
从上面注释可知,HashSet是无序的,LinkedHashSet保持着子元素插入的顺序。而Set是由LinkedHashSet实例化的,所以Set保持着子元素插入的顺序。
如想要深入分析LinkedHashSet,LinkedHashSet源码中有很多external声明的方法,可参考如何找到flutter external声明方法的实现
网友评论