Flutter

flutter Set集合妙用

2021-01-15  本文已影响0人  HawkFlying

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去重

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传送门

上一篇 下一篇

猜你喜欢

热点阅读