系统化掌握Dart编程之集合(Set)

前言

集合 —— 操作批量数据核心工具

Dart中,Set 是一种特殊的集合类型,它确保每个元素都是唯一的不允许重复。就像一个精心整理的工具箱,每种工具只出现一次,方便快速查找和使用。Set 适用于需要去重检查成员资格进行数学集合操作(如并集交集)的场景。通过 Set,可以高效地管理和操作不重复的数据项,为应用程序带来简洁性高性能

千曲而后晓声,观千剑而后识器。虐它千百遍方能通晓其真意

一、基本概念

1.1、图像表示

姓名参加的活动
Alice短跑100米
Bob短跑200米
Charlie长跑5000米

如上图所示,有一个班级的学生名册,确保每个学生的名字只出现一次,不论他们参加了多少个活动。这就是 Set 的工作方式 —— 它是一个无序且不允许重复元素的集合

1.2、定义

Set 是一个无序且不允许重复元素的集合。它类似于数学中的集合概念,提供了一种方便的方式来处理唯一的数据项。

// 创建一个 Set 班级学生名册
Set<String> studentRoster = {'Alice', 'Bob', 'Charlie'};

1.3、特点

  • 1、无序:就像名册中的名字,不能保证每次读取的顺序。
  • 2、不允许重复:每个学生的名字只能出现一次
// 尝试添加重复的名字
studentRoster.add('Alice'); // 不会添加,因为已经存在

// 输出名册
print(studentRoster); // 输出: {Alice, Bob, Charlie}

1.4、泛型支持

DartSet 支持泛型,允许指定列表中元素的具体类型,从而提高代码的类型安全性可读性

// 创建一个泛型为int类型的Set
Set<int> numbers = {1, 2, 3, 4, 5};
// 创建一个泛型为String类型的list
Set<String> names = {'Alice', 'Bob', 'Charlie'};
// 创建一个泛型为dynamic类型的list
Set<dynamic> list = {'Alice', 1, false};

二、创建和初始化

2.1、直接创建

最简单的方式是直接在花括号{}中列出元素

Set<int> numbers = {1, 2, 3, 4, 5};

2.2、使用构造函数

使用 Set 类的构造函数来创建一个空集合具有初始元素的集合

Dart 代码解读复制代码// 创建一个空集合
Set<String> emptySet = <String>{};

// 创建一个包含初始元素的集合
Set<int> initialNumbers = Set<int>.from([1, 2, 3, 4, 5]);

2.3、 使用 Set.of 构造函数

Set.of 可以从任何可迭代对象(如 List)创建一个 Set

Set<int> fromList = Set.of([1, 2, 3, 4, 5]);

三、访问和修改

3.1、访问元素

由于 Set 本身是无序的,它并不支持通过索引访问元素(如 List 那样)。然而,Dart 提供了多种方式来访问 Set 中的元素,主要包括遍历查找特定元素。下面对应的目录中会有介绍,在此不做过多叙述。

3.2、修改元素

由于 Set 的特性,直接修改某个特定元素并不是像 List 那样简单,因为 Set 不支持通过索引访问和修改元素。可以通过以下几种方法来实现对 Set 中元素的“修改”

  • 1、移除旧元素并添加新元素
void main() {
  Set<String> colors = {'红色', '蓝色', '绿色'};
  
  // 修改 "红色" 为 "粉红色"
  if (colors.remove('红色')) {
    colors.add('粉红色');
  }
  
  print(colors); // 输出: {蓝色, 绿色, 粉红色}
}
  • 2、使用 map 方法创建新的 Set:可以使用 map 方法将每个元素映射到新值,然后将结果转换回 Set
void main() {
  Set<int> numbers = {1, 2, 3, 4, 5};
  
  // 将所有数字加 10
  Set<int> updatedNumbers = numbers.map((number) => number + 10).toSet();
  
  print(updatedNumbers); // 输出: {11, 12, 13, 14, 15}
}
  • 3、批量修改(使用 removeWhereaddAll
void main() {
  Set<String> fruits = {'苹果', '香蕉', '橙子'};
  
  // 移除以 "苹" 开头的水果,并添加 "草莓"
  fruits.removeWhere((fruit) => fruit.startsWith('苹'));
  fruits.add('草莓');
  
  print(fruits); // 输出: {香蕉, 橙子, 草莓}
}

3.3、添加元素

使用 addaddAll方法向 Set 中添加新元素。如果元素已经存在,则不会添加。

numbers.add(6);
print(numbers); // 输出: {1, 2, 3, 4, 5, 6

// 使用addAll批量添加元素
numbers.addAll({7, 8, 9});
print(numbers); // 输出: {1, 2, 3, 4, 5, 6, 7, 8, 9}

3.4、移除元素

  • 1、移除指定元素:使用remove方法。
numbers.remove(5);
print(numbers); // 输出: {1, 2, 3, 4, 6, 7, 8, 9}
  • 2、清空集合:使用clear方法。
numbers.clear();
print(numbers); // 输出: {}

四、遍历

4.1、使用for循环

如果确实需要按顺序访问元素,可以先将 Set 转换为 List,然后通过索引访问。

Set<String> colors = {'红色', '蓝色', '绿色'};
  List<String> colorList = colors.toList();
  
for (int i = 0; i < colorList.length; i++) {
  print('颜色 ${i + 1}: ${colorList[i]}');
}

注意:由于 Set 是无序的,转换后的 List 可能不会保持原来的插入顺序。

4.2、使用forEach

forEach 方法允许为 Set 中的每个元素执行一个回调函数

Set<String> colors = {'红色', '蓝色', '绿色'};
colors.forEach((color) => print(color));

4.3、使用for-in循环

for-in 循环提供了一种简洁的方式遍历 Set 中的每个元素。

 Set<int> numbers = {1, 2, 3, 4, 5};
  
 for (var number in numbers) {
    print(number);
} 

五、常用属性和方法

5.1、属性

// length:获取 Set 的大小(元素数量)
print(numbers.length); // 输出: 5
// isEmpty 和 isNotEmpty:检查 Set 是否为空。
print(emptySet.isEmpty); // 输出: true

5.2、常用方法

  • 1、查找特定元素

    • 使用 contains 方法检查某个元素是否存在于 Set 中。
    print(numbers.contains(5)); // 输出: true
    
    • 可以结合 where 方法与 firstlastsingle 来查找符合条件的第一个或最后一个元素,或者唯一符合条件的元素。
    void main() {
      Set<int> numbers = {1, 2, 3, 4, 5};
    
      // 查找第一个大于 3 的元素
      int firstGreaterThanThree = numbers.where((number) => number > 3).first;
      print('第一个大于 3 的数字是: $firstGreaterThanThree');
    
      // 查找唯一等于 4 的元素
      int onlyFour = numbers.singleWhere((number) => number == 4, orElse: () => -1);
      print('唯一的数字 4 是: $onlyFour');
    }
    
    • 使用 anyevery 方法。
    void main() {
      Set<int> numbers = {1, 2, 3, 4, 5};
    
      bool hasEvenNumber = numbers.any((number) => number % 2 == 0);
      print('Set 中是否有偶数: $hasEvenNumber');
    
      bool allPositive = numbers.every((number) => number > 0);
      print('所有数字是否都是正数: $allPositive');
    }
    
  • 2、集合操作

    • 并集:使用 union 方法计算两个 Set 的并集。
    Set<int> setA = {1, 2, 3};
    Set<int> setB = {3, 4, 5};
    Set<int> unionSet = setA.union(setB);
    print(unionSet); // 输出: {1, 2, 3, 4, 5}
    
    • 交集:使用 intersection 方法计算两个 Set 的交集。
    Set<int> intersectionSet = setA.intersection(setB);
    print(intersectionSet); // 输出: {3}
    
    • 差集:使用 difference 方法计算两个 Set 的差集。
    Set<int> differenceSet = setA.difference(setB);
    print(differenceSet); // 输出: {1, 2}
    

六、不可变Set(Set.unmodifiable)

有时需要确保一个 Set 不会被修改。可以使用 Set.unmodifiable 来创建一个不可变的 Set

Set<int> immutableNumbers = Set.unmodifiable({1, 2, 3, 4, 5});

// 下面这行代码会抛出异常,因为 immutableNumbers 是不可变的
// immutableNumbers.add(6);

七、总结

Set 提供了一种强大而灵活的方式来处理唯一的数据项。通过合理利用 Set 的特性,可以编写出更加简洁高效和易于维护的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT枫斗者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值