flutter Set集合妙用

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声明方法的实现

demo传送门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Set集合介绍 Set集合的概念   Set集合类似于一个容器,程序把很多对象保存到Set集合中,Set集合对添加...
    Hughman阅读 212评论 0 1
  • 一,SetSet:注重独一无二的性质,该体系集合可以知道某物是否已近存在于集合中,不会存储重复的元素用于存储无序(...
    zhonghuahan阅读 205评论 0 0
  • 一. ArrayList嵌套 1.定义 在集合中存放集合,和二维数组类似 2.演示 public static v...
    麦小玮阅读 1,872评论 0 0
  • 集合类体系结构 Set集合(接口) set 集合特点 不包含重复元素的集合 没有带索引的方法,所以不能使用普通的f...
    智障猿阅读 293评论 0 0
  • Set接口 set一个最大的特点就是无序,无重复 今天主要内容 HashSetHashSet的工作原理存储自定义对...
    须臾之北阅读 236评论 0 0