Dart Set API 详细总结
基于Dart官方API文档与实践经验整理
目录
概述
在Dart中,Set是一个不包含重复元素的集合,类似于数学中的集合概念。Set具有以下特性:
- 唯一性: 不允许重复元素
- 无序性: 元素没有固定的顺序(除了LinkedHashSet)
- 高效查找: 基于哈希表实现,查找效率高
- 泛型支持: 支持类型安全的泛型操作
- 集合运算: 支持并集、交集、差集等数学运算
创建Set
1. 字面量语法创建
// 创建包含初始值的Set
Set<int> numbers = {1, 2, 3, 4, 5};
Set<String> names = {'Alice', 'Bob', 'Charlie'};
// 创建空Set(需要指定类型)
Set<int> emptyNumbers = <int>{};
Set<String> emptyNames = <String>{};
// 类型推断
var fruits = {'apple', 'banana', 'orange'}; // Set<String>
var scores = {95, 87, 92}; // Set<int>
// 注意:空大括号{}创建的是Map,不是Set
var emptyMap = {}; // Map<dynamic, dynamic>
var emptySet = <String>{}; // Set<String>
创建Set使用注意事项
⚠️ 重要提醒:
-
空集合语法陷阱:
// ❌ 这创建的是Map,不是Set! var wrong = {}; // Map<dynamic, dynamic> // ✅ 正确的空Set创建方式 var correctSet = <String>{}; var anotherSet = Set<String>(); -
重复元素自动去重:
// 重复元素会被自动去除 Set<int> numbers = {1, 2, 2, 3, 3, 3}; print(numbers); // {1, 2, 3}
2. 构造函数创建
// 创建空Set
Set<int> emptySet = Set<int>();
// 参数: 无
// 返回: Set<E> - 新的空Set
// 从其他可迭代对象创建
Set<int> fromIterable = Set<int>.from([1, 2, 3, 2, 1]);
// 参数: elements (Iterable) - 源可迭代对象
// 返回: Set<E> - 新的Set,自动去重
// 结果: {1, 2, 3}
Set<int> ofIterable = Set<int>.of([1, 2, 3, 2, 1]);
// 参数: elements (Iterable<E>) - 源可迭代对象(类型安全)
// 返回: Set<E> - 新的Set,自动去重
// 创建不可变Set
Set<int> unmodifiableSet = Set.unmodifiable([1, 2, 3]);
// 参数: elements (Iterable<E>) - 源可迭代对象
// 返回: Set<E> - 不可修改的Set
// 创建身份Set(基于对象身份而非相等性)
Set<String> identitySet = Set<String>.identity();
// 参数: 无
// 返回: Set<E> - 基于身份比较的Set
构造函数使用注意事项
⚠️ 重要提醒:
-
Set.from vs Set.of的区别:
dynamic dynamicList = [1, 2, 3]; // Set.from 接受任意Iterable,可能有运行时类型错误 Set<int> fromSet = Set<int>.from(dynamicList); // Set.of 要求类型匹配,编译时类型安全 // Set<int> ofSet = Set<int>.of(dynamicList); // 编译错误 Set<int> ofSet = Set<int>.of(<int>[1, 2, 3]); // 正确 -
身份Set vs 相等性Set:
Set<String> normalSet = {'hello', 'world'}; Set<String> identitySet = Set.identity()..addAll(['hello', 'world']); String hello1 = 'hello'; String hello2 = 'hello'; print(normalSet.contains(hello1)); // true print(normalSet.contains(hello2)); // true - 基于值相等 identitySet.clear(); identitySet.add(hello1); print(identitySet.contains(hello1)); // true print(identitySet.contains(hello2)); // true - 字符串字面量共享引用 // 对于对象更明显 class Person { String name; Person(this.name); } Person p1 = Person('Alice'); Person p2 = Person('Alice'); Set<Person> normalPersonSet = {p1, p2}; // 可能包含两个Person(如果没有重写==) Set<Person> identityPersonSet = Set.identity()..addAll([p1, p2]); // 包含两个Person -
不可变Set的限制:
Set<int> immutableSet = Set.unmodifiable([1, 2, 3]); // ❌ 这些操作都会抛出UnsupportedError // immutableSet.add(4); // immutableSet.remove(1); // immutableSet.clear();
基本属性
Set<String> fruits = {'apple', 'banana', 'orange'};
// 长度
print(fruits.length); // 3
// 是否为空
print(fruits.isEmpty); // false
print(fruits.isNotEmpty); // true
// 第一个和最后一个元素(注意:Set是无序的,这些值可能不确定)
print(fruits.first); // 可能是任意元素
print(fruits.last); // 可能是任意元素
// 单个元素(当且仅当Set只有一个元素时)
Set<String> single = {'only'};
print(single.single); // 'only'
// 迭代器
Iterator<String> iterator = fruits.iterator;
基本属性使用注意事项
⚠️ 重要提醒:
-
Set的无序性:
Set<int> numbers = {3, 1, 4, 1, 5}; // 注意:重复的1会被去除 // ❌ 不要依赖Set中元素的顺序 // print(numbers.first); // 结果不确定 // ✅ 如果需要有序,使用LinkedHashSet或转换为List import 'dart:collection'; LinkedHashSet<int> orderedSet = LinkedHashSet<int>.from([3, 1, 4, 1, 5]); print(orderedSet.first); // 3,保持插入顺序 -
空Set访问陷阱:
Set<String> emptySet = <String>{}; // ❌ 这些都会抛出异常 // print(emptySet.first); // StateError // print(emptySet.last); // StateError // print(emptySet.single); // StateError // ✅ 安全的访问方式 if (emptySet.isNotEmpty) { print(emptySet.first); }
添加和删除元素
Set<int> numbers = {1, 2, 3};
// 添加单个元素
bool added = numbers.add(4);
// 参数: value (E) - 要添加的元素
// 返回: bool - 如果元素是新的返回true,如果已存在返回false
print(numbers); // {1, 2, 3, 4}
print(added); // true
// 尝试添加重复元素
bool duplicateAdded = numbers.add(3);
print(duplicateAdded); // false,元素已存在
// 添加多个元素
numbers.addAll([5, 6, 7, 3]); // 3已存在,不会重复添加
// 参数: elements (Iterable<E>) - 要添加的可迭代对象
// 返回: void
print(numbers); // {1, 2, 3, 4, 5, 6, 7}
// 删除单个元素
bool removed = numbers.remove(3);
// 参数: value (Object?) - 要删除的元素
// 返回: bool - 如果成功删除返回true,否则返回false
print(numbers); // {1, 2, 4, 5, 6, 7}
print(removed); // true
// 删除多个元素
numbers.removeAll([1, 2, 8, 9]); // 8, 9不存在,不会报错
// 参数: elements (Iterable<Object?>) - 要删除的可迭代对象
// 返回: void
print(numbers); // {4, 5, 6, 7}
// 只保留指定元素
numbers.retainAll([4, 5, 10, 11]); // 只保留存在的4, 5
// 参数: elements (Iterable<Object?>) - 要保留的可迭代对象
// 返回: void
print(numbers); // {4, 5}
// 根据条件删除
Set<int> values = {1, 2, 3, 4, 5, 6};
values.removeWhere((element) => element % 2 == 0);
// 参数: test (bool Function(E)) - 测试函数,返回true的元素将被删除
// 返回: void
print(values); // {1, 3, 5}
// 根据条件保留
Set<int> scores = {85, 92, 78, 96, 88};
scores.retainWhere((score) => score >= 90);
// 参数: test (bool Function(E)) - 测试函数,返回true的元素将被保留
// 返回: void
print(scores); // {92, 96}
// 清空Set
numbers.clear();
// 参数: 无
// 返回: void
print(numbers); // {}
添加删除使用注意事项
⚠️ 重要提醒:
-
add方法的返回值意义:
Set<String> names = {'Alice', 'Bob'}; bool result1 = names.add('Charlie'); // true - 新元素 bool result2 = names.add('Alice'); // false - 已存在 // ✅ 可以利用返回值判断元素是否已存在 if (!names.add('David')) { print('David已经存在'); } -
删除不存在元素不会报错:
Set<int> numbers = {1, 2, 3}; // ✅ 删除不存在的元素是安全的 bool removed = numbers.remove(999); // false,但不会报错 numbers.removeAll([4, 5, 6]); // 安全,不会报错 -
不可变Set的操作限制:
Set<int> immutableSet = Set.unmodifiable([1, 2, 3]); // ❌ 这些操作都会抛出UnsupportedError // immutableSet.add(4); // immutableSet.remove(1); // immutableSet.addAll([4, 5]); // immutableSet.removeAll([1, 2]); // immutableSet.clear();
查找和检查
Set<String> animals = {'cat', 'dog', 'bird'};
// 检查是否包含元素
print(animals.contains('dog')); // true
print(animals.contains('fish')); // false
// 参数: element (Object?) - 要查找的元素
// 返回: bool - 如果包含该元素返回true,否则返回false
// 检查是否包含任意指定元素
print(animals.containsAll(['cat', 'dog'])); // true
print(animals.containsAll(['cat', 'fish'])); // false
// 参数: other (Iterable<Object?>) - 要检查的元素集合
// 返回: bool - 如果包含所有指定元素返回true,否则返回false
// 查找符合条件的元素
String? found = animals.firstWhere(
(animal) => animal.startsWith('b'),
orElse: () => 'not found'
);
// 参数:
// test (bool Function(E)) - 测试函数
// orElse (E Function()?, 可选) - 未找到时的默认值函数
// 返回: E - 第一个符合条件的元素
print(found); // 'bird'
// 查找符合条件的元素(可能为null)
String? foundOrNull = animals.cast<String?>().firstWhere(
(animal) => animal?.startsWith('z') ?? false,
orElse: () => null
);
print(foundOrNull); // null
// 检查所有元素是否符合条件
Set<int> numbers = {2, 4, 6, 8};
print(numbers.every((n) => n % 2 == 0)); // true
// 参数: test (bool Function(E)) - 测试函数
// 返回: bool - 如果所有元素都符合条件返回true,否则返回false
// 检查是否有元素符合条件
Set<int> mixed = {1, 3, 5, 6};
print(mixed.any((n) => n % 2 == 0)); // true
// 参数: test (bool Function(E)) - 测试函数
// 返回: bool - 如果至少有一个元素符合条件返回true,否则返回false
// 查找元素(返回元素本身或null)
String? lookup = animals.lookup('cat');
// 参数: object (Object?) - 要查找的对象
// 返回: E? - 如果找到返回Set中的元素,否则返回null
print(lookup); // 'cat'
String? notFound = animals.lookup('fish');
print(notFound); // null
查找检查使用注意事项
⚠️ 重要提醒:
-
contains vs lookup的区别:
Set<String> names = {'Alice', 'Bob'}; // contains只返回bool值 bool hasAlice = names.contains('Alice'); // true // lookup返回实际的元素,可用于获取Set中的对象 String? actualAlice = names.lookup('Alice'); // 'Alice' // 对于自定义对象,lookup很有用 class Person { String name; int age; Person(this.name, this.age); @override bool operator ==(Object other) => other is Person && other.name == name; @override int get hashCode => name.hashCode; } Set<Person> people = {Person('Alice', 25), Person('Bob', 30)}; Person? found = people.lookup(Person('Alice', 999)); // 找到age=25的Alice -
firstWhere的异常处理:
Set<int> numbers = {1, 3, 5}; // ❌ 没有orElse时,未找到会抛出StateError try { int even = numbers.firstWhere((n) => n % 2 == 0); } catch (e) { print('未找到偶数: $e'); } // ✅ 提供orElse避免异常 int evenOrDefault = numbers.firstWhere( (n) => n % 2 == 0, orElse: () => -1 ); -
空Set的边界情况:
Set<int> emptySet = <int>{}; // ✅ 这些方法在空Set上是安全的 print(emptySet.contains(1)); // false print(emptySet.containsAll([1, 2])); // false print(emptySet.every((x) => x > 0)); // true! 空集合的全称量词为真 print(emptySet.any((x) => x > 0)); // false
集合运算
Set<int> set1 = {1, 2, 3, 4};
Set<int> set2 = {3, 4, 5, 6};
Set<int> set3 = {4, 5, 6, 7};
// 并集(union)- 包含两个集合的所有元素
Set<int> unionResult = set1.union(set2);
// 参数: other (Set<E>) - 另一个集合
// 返回: Set<E> - 新的包含两个集合所有元素的Set
print(unionResult); // {1, 2, 3, 4, 5, 6}
// 交集(intersection)- 包含两个集合的共同元素
Set<int> intersectionResult = set1.intersection(set2);
// 参数: other (Set<Object?>) - 另一个集合
// 返回: Set<E> - 新的包含共同元素的Set
print(intersectionResult); // {3, 4}
// 差集(difference)- 包含在第一个集合但不在第二个集合中的元素
Set<int> differenceResult = set1.difference(set2);
// 参数: other (Set<Object?>) - 另一个集合
// 返回: Set<E> - 新的包含差集元素的Set
print(differenceResult); // {1, 2}
// 多个集合的运算
Set<int> multiUnion = set1.union(set2).union(set3);
print(multiUnion); // {1, 2, 3, 4, 5, 6, 7}
Set<int> multiIntersection = set1.intersection(set2).intersection(set3);
print(multiIntersection); // {4}
集合运算使用注意事项
⚠️ 重要提醒:
-
集合运算返回新Set:
Set<int> original = {1, 2, 3}; Set<int> other = {2, 3, 4}; Set<int> result = original.union(other); // ✅ 原始集合不会被修改 print(original); // {1, 2, 3} - 保持不变 print(result); // {1, 2, 3, 4} - 新的集合 -
空集合的运算规律:
Set<int> numbers = {1, 2, 3}; Set<int> empty = <int>{}; print(numbers.union(empty)); // {1, 2, 3} - 并集 print(numbers.intersection(empty)); // {} - 交集为空 print(numbers.difference(empty)); // {1, 2, 3} - 差集是原集合 print(empty.difference(numbers)); // {} - 空集合的差集为空 -
类型兼容性:
Set<int> integers = {1, 2, 3}; Set<num> numbers = {2.5, 3, 4}; // ✅ 可以与超类型进行运算 Set<int> result = integers.intersection(numbers); // {3} // ❌ 但要注意类型安全 // Set<int> badResult = integers.union(numbers); // 类型错误
转换和映射
Set<int> numbers = {1, 2, 3, 4, 5};
// map: 转换每个元素
Set<int> doubled = numbers.map((n) => n * 2).toSet();
// 参数: f (T Function(E)) - 转换函数,接收元素返回转换后的值
// 返回: Iterable<T> - 转换后的可迭代对象(需要toSet()转换为Set)
print(doubled); // {2, 4, 6, 8, 10}
// where: 过滤元素
Set<int> evenNumbers = numbers.where((n) => n % 2 == 0).toSet();
// 参数: test (bool Function(E)) - 测试函数,返回true的元素将被保留
// 返回: Iterable<E> - 过滤后的可迭代对象
print(evenNumbers); // {2, 4}
// expand: 展开元素
Set<List<int>> nested = {{1, 2}, {3, 4}, {5}};
Set<int> flattened = nested.expand((set) => set).toSet();
// 参数: f (Iterable<T> Function(E)) - 展开函数,接收元素返回可迭代对象
// 返回: Iterable<T> - 展开后的可迭代对象
print(flattened); // {1, 2, 3, 4, 5}
// fold: 累积操作
int sum = numbers.fold(0, (prev, element) => prev + element);
// 参数:
// initialValue (T) - 初始值
// combine (T Function(T, E)) - 累积函数,接收前一个值和当前元素
// 返回: T - 累积结果
print(sum); // 15
// reduce: 归约操作
int product = numbers.reduce((value, element) => value * element);
// 参数: combine (E Function(E, E)) - 归约函数,接收两个元素返回归约结果
// 返回: E - 归约结果
// 注意: 如果Set为空会抛出异常
print(product); // 120
// 转换为其他集合类型
List<int> numbersList = numbers.toList();
// 参数: 无
// 返回: List<E> - 转换后的List
print(numbersList); // [1, 2, 3, 4, 5] (顺序可能不同)
// 转换为字符串
String setString = numbers.join(', ');
// 参数: separator (String, 可选) - 分隔符,默认为空字符串
// 返回: String - 连接后的字符串
print(setString); // "1, 2, 3, 4, 5" (顺序可能不同)
转换映射使用注意事项
⚠️ 重要提醒:
-
map操作可能产生重复元素:
Set<int> numbers = {1, 2, 3, 4, 5}; // ❌ map后可能有重复元素 Iterable<int> halves = numbers.map((n) => n ~/ 2); // [0, 1, 1, 2, 2] Set<int> uniqueHalves = halves.toSet(); // {0, 1, 2} - 自动去重 print(halves.length); // 5 print(uniqueHalves.length); // 3 -
惰性求值的注意事项:
Set<int> numbers = {1, 2, 3, 4, 5}; // ❌ 这些操作是惰性的,不会立即执行 Iterable<int> doubled = numbers.map((n) => n * 2); Iterable<int> evens = numbers.where((n) => n % 2 == 0); // ✅ 需要调用toSet()或toList()才会实际执行 Set<int> doubledSet = numbers.map((n) => n * 2).toSet(); Set<int> evensSet = numbers.where((n) => n % 2 == 0).toSet(); -
reduce在空Set上的异常:
Set<int> numbers = {1, 2, 3, 4, 5}; Set<int> emptySet = <int>{}; // ✅ fold可以处理空Set int sum1 = numbers.fold(0, (a, b) => a + b); // 15 int sum2 = emptySet.fold(0, (a, b) => a + b); // 0 // ❌ reduce在空Set上会抛出异常 int sum3 = numbers.reduce((a, b) => a + b); // 15 // int sum4 = emptySet.reduce((a, b) => a + b); // StateError! // ✅ 安全的reduce使用 if (emptySet.isNotEmpty) { int sum = emptySet.reduce((a, b) => a + b); }
高级操作
类型转换和过滤
Set<dynamic> mixed = {1, 'hello', 2, 'world', 3.14, true};
// 类型过滤
Set<int> integers = mixed.whereType<int>().toSet();
// 参数: 无(泛型参数指定类型)
// 返回: Iterable<T> - 指定类型的元素
print(integers); // {1, 2}
Set<String> strings = mixed.whereType<String>().toSet();
print(strings); // {'hello', 'world'}
// 类型转换
Set<String> stringified = mixed.map((e) => e.toString()).toSet();
print(stringified); // {'1', 'hello', '2', 'world', '3.14', 'true'}
集合比较
Set<int> set1 = {1, 2, 3};
Set<int> set2 = {1, 2, 3};
Set<int> set3 = {3, 2, 1}; // 顺序不同
Set<int> set4 = {1, 2, 3, 4};
// Set相等性比较(基于内容,不考虑顺序)
print(set1 == set2); // true
print(set1 == set3); // true - Set不考虑顺序
print(set1 == set4); // false
// 检查子集关系
bool isSubset(Set<E> subset, Set<E> superset) {
return subset.difference(superset).isEmpty;
}
bool isSupersetOf(Set<E> superset, Set<E> subset) {
return subset.difference(superset).isEmpty;
}
print(isSubset(set1, set4)); // true - set1是set4的子集
print(isSupersetOf(set4, set1)); // true - set4是set1的超集
自定义Set实现
import 'dart:collection';
// LinkedHashSet - 保持插入顺序
LinkedHashSet<String> orderedSet = LinkedHashSet<String>();
orderedSet.addAll(['third', 'first', 'second']);
print(orderedSet.toList()); // [third, first, second] - 保持插入顺序
// SplayTreeSet - 自动排序
SplayTreeSet<int> sortedSet = SplayTreeSet<int>();
sortedSet.addAll([3, 1, 4, 1, 5, 9]);
print(sortedSet.toList()); // [1, 3, 4, 5, 9] - 自动排序且去重
// 自定义比较器的SplayTreeSet
SplayTreeSet<String> customSortedSet = SplayTreeSet<String>(
(a, b) => b.compareTo(a) // 反向排序
);
customSortedSet.addAll(['banana', 'apple', 'cherry']);
print(customSortedSet.toList()); // [cherry, banana, apple]
高级操作使用注意事项
⚠️ 重要提醒:
-
不同Set实现的性能特点:
// HashSet - 最快的查找,无序 Set<int> hashSet = <int>{}; // 默认实现 // LinkedHashSet - 保持插入顺序,稍慢 import 'dart:collection'; LinkedHashSet<int> linkedSet = LinkedHashSet<int>(); // SplayTreeSet - 自动排序,查找O(log n) SplayTreeSet<int> treeSet = SplayTreeSet<int>(); // 性能对比(大概): // 插入: HashSet > LinkedHashSet > SplayTreeSet // 查找: HashSet ≈ LinkedHashSet > SplayTreeSet // 遍历: 都是O(n),但SplayTreeSet是有序的 -
自定义对象的哈希和相等性:
class Person { String name; int age; Person(this.name, this.age); // ✅ 必须同时重写==和hashCode @override bool operator ==(Object other) => other is Person && other.name == name && other.age == age; @override int get hashCode => Object.hash(name, age); @override String toString() => 'Person($name, $age)'; } Set<Person> people = { Person('Alice', 25), Person('Bob', 30), Person('Alice', 25), // 重复,会被去除 }; print(people.length); // 2 -
类型过滤的安全性:
Set<dynamic> mixed = {1, 'hello', null, 2.5}; // ✅ whereType是安全的 Set<int> integers = mixed.whereType<int>().toSet(); // ❌ 强制转换可能出错 // Set<int> forced = mixed.map((e) => e as int).toSet(); // 运行时错误! // ✅ 安全的转换 Set<int> safe = mixed .where((e) => e is int) .map((e) => e as int) .toSet();
方法参数速查表
构造方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
Set() | 无 | 创建空Set | Set<E> |
Set.from() | elements: Iterable | 从可迭代对象创建 | Set<E> |
Set.of() | elements: Iterable<E> | 从可迭代对象创建(类型安全) | Set<E> |
Set.unmodifiable() | elements: Iterable<E> | 创建不可修改Set | Set<E> |
Set.identity() | 无 | 创建基于身份比较的Set | Set<E> |
添加删除方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
add() | value: E | 添加元素 | bool |
addAll() | elements: Iterable<E> | 添加多个元素 | void |
remove() | value: Object? | 删除元素 | bool |
removeAll() | elements: Iterable<Object?> | 删除多个元素 | void |
retainAll() | elements: Iterable<Object?> | 只保留指定元素 | void |
removeWhere() | test: bool Function(E) | 根据条件删除 | void |
retainWhere() | test: bool Function(E) | 根据条件保留 | void |
clear() | 无 | 清空Set | void |
查找检查方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
contains() | element: Object? | 检查是否包含元素 | bool |
containsAll() | other: Iterable<Object?> | 检查是否包含所有元素 | bool |
lookup() | object: Object? | 查找元素 | E? |
firstWhere() | test: bool Function(E) orElse: E Function()? | 查找符合条件的元素 | E |
every() | test: bool Function(E) | 检查所有元素是否符合条件 | bool |
any() | test: bool Function(E) | 检查是否有元素符合条件 | bool |
集合运算方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
union() | other: Set<E> | 并集运算 | Set<E> |
intersection() | other: Set<Object?> | 交集运算 | Set<E> |
difference() | other: Set<Object?> | 差集运算 | Set<E> |
转换映射方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
map() | f: T Function(E) | 转换每个元素 | Iterable<T> |
where() | test: bool Function(E) | 过滤元素 | Iterable<E> |
whereType() | 无(泛型参数) | 类型过滤 | Iterable<T> |
expand() | f: Iterable<T> Function(E) | 展开元素 | Iterable<T> |
fold() | initialValue: T combine: T Function(T, E) | 累积操作 | T |
reduce() | combine: E Function(E, E) | 归约操作 | E |
join() | separator: String | 转换为字符串 | String |
toList() | 无 | 转换为List | List<E> |
性能考虑
时间复杂度详解
| 操作 | HashSet | LinkedHashSet | SplayTreeSet | 说明 |
|---|---|---|---|---|
add() | O(1)* | O(1)* | O(log n) | *摊销时间,最坏O(n) |
remove() | O(1)* | O(1)* | O(log n) | *摊销时间,最坏O(n) |
contains() | O(1)* | O(1)* | O(log n) | 查找操作 |
lookup() | O(1)* | O(1)* | O(log n) | 查找并返回元素 |
clear() | O(n) | O(n) | O(n) | 清空所有元素 |
union() | O(n+m) | O(n+m) | O(n log m + m log n) | n,m为两个集合大小 |
intersection() | O(min(n,m)) | O(min(n,m)) | O(n log m) | 取较小集合遍历 |
difference() | O(n) | O(n) | O(n log m) | n为第一个集合大小 |
| 遍历 | O(n) | O(n) | O(n) | 线性遍历 |
| 排序输出 | O(n log n) | O(n log n) | O(n) | TreeSet已排序 |
*摊销时间复杂度,哈希冲突时可能退化
空间复杂度
| Set类型 | 空间复杂度 | 额外开销 | 说明 |
|---|---|---|---|
| HashSet | O(n) | ~25-50% | 哈希表空间,负载因子影响 |
| LinkedHashSet | O(n) | ~50-75% | 额外的链表指针 |
| SplayTreeSet | O(n) | ~100% | 树节点指针开销 |
性能基准测试
import 'dart:collection';
import 'dart:math';
/// 性能测试工具类
class SetPerformanceTester {
static const int testSize = 100000;
static final Random _random = Random();
/// 测试不同Set实现的插入性能
static void testInsertPerformance() {
print('=== 插入性能测试 (${testSize}个元素) ===');
// 生成测试数据
List<int> testData = List.generate(testSize, (i) => _random.nextInt(testSize * 2));
// HashSet测试
Stopwatch sw = Stopwatch()..start();
Set<int> hashSet = <int>{};
for (int value in testData) {
hashSet.add(value);
}
sw.stop();
print('HashSet插入: ${sw.elapsedMilliseconds}ms, 元素数: ${hashSet.length}');
// LinkedHashSet测试
sw.reset()..start();
LinkedHashSet<int> linkedSet = LinkedHashSet<int>();
for (int value in testData) {
linkedSet.add(value);
}
sw.stop();
print('LinkedHashSet插入: ${sw.elapsedMilliseconds}ms, 元素数: ${linkedSet.length}');
// SplayTreeSet测试
sw.reset()..start();
SplayTreeSet<int> treeSet = SplayTreeSet<int>();
for (int value in testData) {
treeSet.add(value);
}
sw.stop();
print('SplayTreeSet插入: ${sw.elapsedMilliseconds}ms, 元素数: ${treeSet.length}');
}
/// 测试查找性能
static void testLookupPerformance() {
print('\n=== 查找性能测试 ===');
// 准备数据
List<int> data = List.generate(testSize, (i) => i);
Set<int> hashSet = data.toSet();
LinkedHashSet<int> linkedSet = LinkedHashSet.from(data);
SplayTreeSet<int> treeSet = SplayTreeSet.from(data);
// 生成查找目标
List<int> searchTargets = List.generate(10000, (i) => _random.nextInt(testSize));
// HashSet查找
Stopwatch sw = Stopwatch()..start();
int found = 0;
for (int target in searchTargets) {
if (hashSet.contains(target)) found++;
}
sw.stop();
print('HashSet查找: ${sw.elapsedMilliseconds}ms, 找到: $found');
// LinkedHashSet查找
sw.reset()..start();
found = 0;
for (int target in searchTargets) {
if (linkedSet.contains(target)) found++;
}
sw.stop();
print('LinkedHashSet查找: ${sw.elapsedMilliseconds}ms, 找到: $found');
// SplayTreeSet查找
sw.reset()..start();
found = 0;
for (int target in searchTargets) {
if (treeSet.contains(target)) found++;
}
sw.stop();
print('SplayTreeSet查找: ${sw.elapsedMilliseconds}ms, 找到: $found');
}
/// 测试集合运算性能
static void testSetOperationsPerformance() {
print('\n=== 集合运算性能测试 ===');
Set<int> set1 = Set.from(List.generate(testSize ~/ 2, (i) => i));
Set<int> set2 = Set.from(List.generate(testSize ~/ 2, (i) => i + testSize ~/ 4));
// 并集测试
Stopwatch sw = Stopwatch()..start();
Set<int> unionResult = set1.union(set2);
sw.stop();
print('并集运算: ${sw.elapsedMilliseconds}ms, 结果大小: ${unionResult.length}');
// 交集测试
sw.reset()..start();
Set<int> intersectionResult = set1.intersection(set2);
sw.stop();
print('交集运算: ${sw.elapsedMilliseconds}ms, 结果大小: ${intersectionResult.length}');
// 差集测试
sw.reset()..start();
Set<int> differenceResult = set1.difference(set2);
sw.stop();
print('差集运算: ${sw.elapsedMilliseconds}ms, 结果大小: ${differenceResult.length}');
}
}
内存使用优化
1. 选择合适的初始容量
/// 内存优化的Set创建
class OptimizedSetCreation {
/// 根据预期大小创建HashSet
static Set<T> createOptimalHashSet<T>(int expectedSize) {
// Dart的HashSet会自动调整大小,但预估有助于减少rehashing
if (expectedSize <= 100) {
return <T>{}; // 小集合使用默认
} else if (expectedSize <= 10000) {
// 中等大小集合,可以考虑预分配
return <T>{};
} else {
// 大集合,考虑分批处理
return <T>{};
}
}
/// 内存敏感的大集合处理
static Set<T> createLargeSet<T>(Iterable<T> source) {
Set<T> result = <T>{};
// 分批添加,避免一次性分配大量内存
const int batchSize = 1000;
List<T> batch = [];
for (T item in source) {
batch.add(item);
if (batch.length >= batchSize) {
result.addAll(batch);
batch.clear(); // 及时释放临时内存
}
}
if (batch.isNotEmpty) {
result.addAll(batch);
}
return result;
}
}
2. 内存泄漏防护
/// 防止内存泄漏的Set管理
class MemorySafeSetManager<T> {
Set<T> _items = <T>{};
static const int maxSize = 10000;
/// 安全添加元素,防止无限增长
bool add(T item) {
if (_items.length >= maxSize) {
// 实现LRU策略或简单清理
_clearOldest();
}
return _items.add(item);
}
/// 清理最旧的元素(简化版LRU)
void _clearOldest() {
if (_items.isNotEmpty) {
// 简单策略:清理一半元素
List<T> itemsList = _items.toList();
_items.clear();
_items.addAll(itemsList.skip(itemsList.length ~/ 2));
}
}
/// 定期清理
void periodicCleanup() {
if (_items.length > maxSize * 0.8) {
_clearOldest();
}
}
/// 释放资源
void dispose() {
_items.clear();
}
}
高级性能优化技巧
1. 哈希函数优化
/// 优化的自定义对象哈希实现
class OptimizedPerson {
final String name;
final int age;
final String email;
// 缓存哈希值,避免重复计算
int? _cachedHashCode;
OptimizedPerson(this.name, this.age, this.email);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is OptimizedPerson &&
runtimeType == other.runtimeType &&
name == other.name &&
age == other.age &&
email == other.email;
@override
int get hashCode {
// 使用缓存的哈希值
return _cachedHashCode ??= Object.hash(name, age, email);
}
/// 高性能哈希实现(如果字段较多)
static int fastHash(String name, int age, String email) {
// 使用位运算优化哈希计算
int hash = name.hashCode;
hash = hash * 31 + age;
hash = hash * 31 + email.hashCode;
return hash;
}
}
/// 针对特定场景的高性能哈希
class FastIntPair {
final int x, y;
const FastIntPair(this.x, this.y);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FastIntPair && x == other.x && y == other.y;
@override
int get hashCode {
// 使用位移操作快速计算哈希
return (x << 16) ^ y;
}
}
2. 批量操作优化
/// 高效的批量Set操作
extension SetBatchOperations<T> on Set<T> {
/// 高效的批量添加
void addAllOptimized(Iterable<T> elements) {
if (elements.isEmpty) return;
// 对于大量元素,分批处理以避免内存峰值
if (elements.length > 1000) {
const int batchSize = 1000;
Iterator<T> iterator = elements.iterator;
while (iterator.moveNext()) {
Set<T> batch = <T>{};
batch.add(iterator.current);
// 收集一批元素
for (int i = 1; i < batchSize && iterator.moveNext(); i++) {
batch.add(iterator.current);
}
// 批量添加
addAll(batch);
}
} else {
addAll(elements);
}
}
/// 高效的条件批量删除
void removeWhereOptimized(bool Function(T) test) {
// 先收集要删除的元素,避免迭代时修改
List<T> toRemove = where(test).toList();
// 批量删除
if (toRemove.isNotEmpty) {
removeAll(toRemove);
}
}
/// 高效的集合运算
Set<T> unionOptimized(Set<T> other) {
// 优化:总是以较大的集合为基础
if (length >= other.length) {
return Set<T>.from(this)..addAll(other);
} else {
return Set<T>.from(other)..addAll(this);
}
}
Set<T> intersectionOptimized(Set<T> other) {
// 优化:遍历较小的集合
Set<T> smaller = length <= other.length ? this : other;
Set<T> larger = length <= other.length ? other : this;
return smaller.where(larger.contains).toSet();
}
}
3. 并发安全的Set操作
import 'dart:async';
/// 线程安全的Set包装器
class ConcurrentSet<T> {
final Set<T> _set = <T>{};
final Completer<void>? _lock = null; // 简化示例
/// 线程安全的添加操作
Future<bool> add(T value) async {
// 在实际应用中需要使用适当的同步机制
return _set.add(value);
}
/// 线程安全的查询操作
bool contains(T value) {
return _set.contains(value);
}
/// 获取快照,避免并发修改
Set<T> snapshot() {
return Set<T>.from(_set);
}
}
性能监控和调试
1. 性能分析工具
/// Set性能监控工具
class SetPerformanceMonitor<T> {
final Set<T> _set;
int _addCount = 0;
int _removeCount = 0;
int _lookupCount = 0;
final Stopwatch _totalTime = Stopwatch();
SetPerformanceMonitor(this._set);
bool add(T value) {
_totalTime.start();
_addCount++;
bool result = _set.add(value);
_totalTime.stop();
return result;
}
bool remove(T value) {
_totalTime.start();
_removeCount++;
bool result = _set.remove(value);
_totalTime.stop();
return result;
}
bool contains(T value) {
_totalTime.start();
_lookupCount++;
bool result = _set.contains(value);
_totalTime.stop();
return result;
}
/// 获取性能统计
void printStats() {
print('Set性能统计:');
print(' 总操作时间: ${_totalTime.elapsedMilliseconds}ms');
print(' 添加操作: $_addCount 次');
print(' 删除操作: $_removeCount 次');
print(' 查找操作: $_lookupCount 次');
print(' 当前大小: ${_set.length}');
print(' 平均操作时间: ${_totalTime.elapsedMicroseconds / (_addCount + _removeCount + _lookupCount)}μs');
}
}
2. 内存使用分析
/// 内存使用分析工具
class SetMemoryAnalyzer {
/// 估算Set的内存使用
static int estimateMemoryUsage<T>(Set<T> set) {
int baseSize = 0;
// 基础开销估算
if (set is LinkedHashSet) {
baseSize = set.length * 24; // 估算:每个元素额外的指针开销
} else if (set.runtimeType.toString().contains('SplayTree')) {
baseSize = set.length * 32; // 估算:树节点开销
} else {
baseSize = set.length * 16; // 估算:哈希表开销
}
// 元素内容大小(简化估算)
int contentSize = 0;
if (set.isNotEmpty) {
T sample = set.first;
if (sample is String) {
contentSize = set.cast<String>().fold(0, (sum, s) => sum + s.length * 2);
} else if (sample is int) {
contentSize = set.length * 8;
} else {
contentSize = set.length * 8; // 引用大小
}
}
return baseSize + contentSize;
}
/// 分析不同Set实现的内存效率
static void compareMemoryEfficiency<T>(Iterable<T> data) {
print('=== 内存效率对比 ===');
Set<T> hashSet = data.toSet();
LinkedHashSet<T> linkedSet = LinkedHashSet.from(data);
SplayTreeSet<T> treeSet = SplayTreeSet.from(data);
print('数据量: ${data.length}');
print('HashSet内存估算: ${estimateMemoryUsage(hashSet)} bytes');
print('LinkedHashSet内存估算: ${estimateMemoryUsage(linkedSet)} bytes');
print('SplayTreeSet内存估算: ${estimateMemoryUsage(treeSet)} bytes');
}
}
实际应用场景的性能优化
1. 缓存系统优化
/// 高性能缓存Set
class PerformanceCache<T> {
final Set<T> _cache = <T>{};
final int maxSize;
int _accessCount = 0;
PerformanceCache({this.maxSize = 1000});
bool add(T item) {
_accessCount++;
// 定期清理,避免频繁检查
if (_accessCount % 100 == 0 && _cache.length > maxSize) {
_performCleanup();
}
return _cache.add(item);
}
void _performCleanup() {
// 简单的清理策略:保留一半元素
if (_cache.length > maxSize) {
List<T> items = _cache.toList();
_cache.clear();
_cache.addAll(items.take(maxSize ~/ 2));
}
}
bool contains(T item) => _cache.contains(item);
void clear() {
_cache.clear();
_accessCount = 0;
}
}
2. 数据去重优化
/// 高效的数据去重工具
class DataDeduplicator<T> {
/// 内存效率优先的去重
static Set<T> deduplicateMemoryEfficient<T>(Iterable<T> data) {
Set<T> result = <T>{};
for (T item in data) {
result.add(item);
// 定期检查内存使用,防止内存爆炸
if (result.length % 10000 == 0) {
// 在实际应用中可以添加内存检查逻辑
print('当前去重数据量: ${result.length}');
}
}
return result;
}
/// 速度优先的去重
static Set<T> deduplicateSpeedOptimized<T>(List<T> data) {
// 预估大小,减少rehashing
Set<T> result = <T>{};
// 批量处理
const int batchSize = 1000;
for (int i = 0; i < data.length; i += batchSize) {
int end = (i + batchSize > data.length) ? data.length : i + batchSize;
result.addAll(data.sublist(i, end));
}
return result;
}
}
性能测试和基准
/// 运行完整的性能测试套件
void runPerformanceTestSuite() {
print('开始Set性能测试套件...\n');
// 插入性能测试
SetPerformanceTester.testInsertPerformance();
// 查找性能测试
SetPerformanceTester.testLookupPerformance();
// 集合运算性能测试
SetPerformanceTester.testSetOperationsPerformance();
// 内存效率测试
List<int> testData = List.generate(10000, (i) => i);
SetMemoryAnalyzer.compareMemoryEfficiency(testData);
print('\n性能测试完成!');
}
性能优化最佳实践总结
-
选择合适的Set实现
- 频繁查找:使用HashSet
- 需要保持顺序:使用LinkedHashSet
- 需要排序:使用SplayTreeSet
-
优化哈希函数
- 缓存哈希值避免重复计算
- 使用高效的哈希算法
- 确保哈希分布均匀
-
批量操作优化
- 使用addAll而不是循环add
- 分批处理大量数据
- 预估集合大小
-
内存管理
- 及时清理不需要的元素
- 限制集合最大大小
- 监控内存使用情况
-
并发安全
- 使用适当的同步机制
- 获取快照避免并发修改
- 考虑使用不可变Set
通过这些优化技巧,可以显著提升Set操作的性能和内存效率。
性能分析和调试指南
1. 使用Dart Observatory进行性能分析
/// 性能分析示例代码
import 'dart:developer' as developer;
class SetProfiler<T> {
final Set<T> _set;
final String _name;
SetProfiler(this._set, this._name);
/// 带性能标记的添加操作
bool addWithProfiling(T value) {
return developer.Timeline.timeSync('$_name.add', () {
return _set.add(value);
});
}
/// 带性能标记的查找操作
bool containsWithProfiling(T value) {
return developer.Timeline.timeSync('$_name.contains', () {
return _set.contains(value);
});
}
/// 带性能标记的集合运算
Set<T> unionWithProfiling(Set<T> other) {
return developer.Timeline.timeSync('$_name.union', () {
return _set.union(other);
});
}
}
/// 使用示例
void profileSetOperations() {
Set<int> testSet = <int>{};
SetProfiler<int> profiler = SetProfiler(testSet, 'TestSet');
// 这些操作会在Dart Observatory中显示时间线
for (int i = 0; i < 10000; i++) {
profiler.addWithProfiling(i);
}
for (int i = 0; i < 1000; i++) {
profiler.containsWithProfiling(i * 10);
}
}
2. 内存使用监控
import 'dart:io';
/// 内存使用监控工具
class MemoryMonitor {
static void logMemoryUsage(String operation) {
ProcessInfo info = ProcessInfo.currentRss;
int memoryMB = info ~/ (1024 * 1024);
print('[$operation] 当前内存使用: ${memoryMB}MB');
}
/// 监控Set操作的内存影响
static void monitorSetMemory<T>(Set<T> set, String setName) {
print('=== $setName 内存监控 ===');
logMemoryUsage('初始状态');
// 模拟大量数据添加
List<T> testData = _generateTestData<T>(100000);
logMemoryUsage('测试数据生成完成');
set.addAll(testData);
logMemoryUsage('添加${testData.length}个元素后');
// 清理一半数据
List<T> toRemove = set.take(set.length ~/ 2).toList();
set.removeAll(toRemove);
logMemoryUsage('删除${toRemove.length}个元素后');
set.clear();
logMemoryUsage('清空Set后');
}
static List<T> _generateTestData<T>(int count) {
if (T == int) {
return List.generate(count, (i) => i).cast<T>();
} else if (T == String) {
return List.generate(count, (i) => 'item_$i').cast<T>();
}
throw UnsupportedError('不支持的类型: $T');
}
}
3. 性能基准测试框架
/// 性能基准测试框架
class SetBenchmark<T> {
final String name;
final Set<T> Function() setFactory;
final List<T> testData;
SetBenchmark(this.name, this.setFactory, this.testData);
/// 运行完整的性能基准测试
BenchmarkResult runBenchmark() {
BenchmarkResult result = BenchmarkResult(name);
// 插入性能测试
result.insertTime = _benchmarkInsert();
// 查找性能测试
result.lookupTime = _benchmarkLookup();
// 删除性能测试
result.removeTime = _benchmarkRemove();
// 内存使用估算
result.memoryUsage = _estimateMemoryUsage();
return result;
}
int _benchmarkInsert() {
Set<T> set = setFactory();
Stopwatch sw = Stopwatch()..start();
for (T item in testData) {
set.add(item);
}
sw.stop();
return sw.elapsedMicroseconds;
}
int _benchmarkLookup() {
Set<T> set = setFactory();
set.addAll(testData);
Stopwatch sw = Stopwatch()..start();
for (T item in testData.take(1000)) {
set.contains(item);
}
sw.stop();
return sw.elapsedMicroseconds;
}
int _benchmarkRemove() {
Set<T> set = setFactory();
set.addAll(testData);
List<T> toRemove = testData.take(testData.length ~/ 2).toList();
Stopwatch sw = Stopwatch()..start();
for (T item in toRemove) {
set.remove(item);
}
sw.stop();
return sw.elapsedMicroseconds;
}
int _estimateMemoryUsage() {
Set<T> set = setFactory();
set.addAll(testData);
// 简化的内存估算
int baseSize = set.length * 16; // 基础开销
int contentSize = 0;
if (testData.isNotEmpty) {
T sample = testData.first;
if (sample is String) {
contentSize = testData.cast<String>().fold(0, (sum, s) => sum + s.length * 2);
} else if (sample is int) {
contentSize = testData.length * 8;
}
}
return baseSize + contentSize;
}
}
/// 基准测试结果
class BenchmarkResult {
final String setType;
int insertTime = 0;
int lookupTime = 0;
int removeTime = 0;
int memoryUsage = 0;
BenchmarkResult(this.setType);
void printResults() {
print('=== $setType 性能基准测试结果 ===');
print('插入时间: ${insertTime}μs');
print('查找时间: ${lookupTime}μs');
print('删除时间: ${removeTime}μs');
print('内存使用: ${memoryUsage} bytes');
print('总体评分: ${_calculateScore()}');
print();
}
double _calculateScore() {
// 简化的评分算法(数值越小越好)
double timeScore = (insertTime + lookupTime + removeTime) / 3.0;
double memoryScore = memoryUsage.toDouble();
return (timeScore + memoryScore / 1000) / 2;
}
}
/// 运行多种Set实现的对比测试
void runSetBenchmarkSuite() {
List<int> testData = List.generate(50000, (i) => i);
List<SetBenchmark<int>> benchmarks = [
SetBenchmark('HashSet', () => <int>{}, testData),
SetBenchmark('LinkedHashSet', () => LinkedHashSet<int>(), testData),
SetBenchmark('SplayTreeSet', () => SplayTreeSet<int>(), testData),
];
print('开始Set性能基准测试...\n');
for (SetBenchmark<int> benchmark in benchmarks) {
BenchmarkResult result = benchmark.runBenchmark();
result.printResults();
}
}
4. 性能回归测试
/// 性能回归测试工具
class PerformanceRegressionTester {
final Map<String, BenchmarkResult> _baselineResults = {};
/// 设置基准性能数据
void setBaseline(String testName, BenchmarkResult result) {
_baselineResults[testName] = result;
}
/// 运行回归测试
void runRegressionTest(String testName, BenchmarkResult currentResult) {
BenchmarkResult? baseline = _baselineResults[testName];
if (baseline == null) {
print('警告: 没有找到 $testName 的基准数据');
return;
}
print('=== $testName 性能回归测试 ===');
double insertChange = _calculatePercentageChange(
baseline.insertTime, currentResult.insertTime);
double lookupChange = _calculatePercentageChange(
baseline.lookupTime, currentResult.lookupTime);
double removeChange = _calculatePercentageChange(
baseline.removeTime, currentResult.removeTime);
double memoryChange = _calculatePercentageChange(
baseline.memoryUsage, currentResult.memoryUsage);
print('插入性能变化: ${insertChange.toStringAsFixed(1)}%');
print('查找性能变化: ${lookupChange.toStringAsFixed(1)}%');
print('删除性能变化: ${removeChange.toStringAsFixed(1)}%');
print('内存使用变化: ${memoryChange.toStringAsFixed(1)}%');
// 检查是否有显著的性能退化
if (insertChange > 10 || lookupChange > 10 ||
removeChange > 10 || memoryChange > 10) {
print('⚠️ 检测到显著的性能退化!');
} else if (insertChange < -10 || lookupChange < -10 ||
removeChange < -10 || memoryChange < -10) {
print('✅ 检测到性能改进!');
} else {
print('✅ 性能保持稳定');
}
print();
}
double _calculatePercentageChange(int baseline, int current) {
if (baseline == 0) return 0;
return ((current - baseline) / baseline) * 100;
}
}
5. 实时性能监控
/// 实时性能监控器
class RealTimeSetMonitor<T> {
final Set<T> _set;
final String _name;
int _operationCount = 0;
int _totalTime = 0;
final Stopwatch _stopwatch = Stopwatch();
RealTimeSetMonitor(this._set, this._name);
bool add(T value) {
_startOperation();
bool result = _set.add(value);
_endOperation('add');
return result;
}
bool remove(T value) {
_startOperation();
bool result = _set.remove(value);
_endOperation('remove');
return result;
}
bool contains(T value) {
_startOperation();
bool result = _set.contains(value);
_endOperation('contains');
return result;
}
void _startOperation() {
_stopwatch.reset();
_stopwatch.start();
}
void _endOperation(String operation) {
_stopwatch.stop();
_operationCount++;
_totalTime += _stopwatch.elapsedMicroseconds;
// 每1000次操作输出一次统计
if (_operationCount % 1000 == 0) {
_printStats();
}
}
void _printStats() {
double avgTime = _totalTime / _operationCount;
print('[$_name] 操作统计: 总计${_operationCount}次, '
'平均耗时${avgTime.toStringAsFixed(2)}μs, '
'当前大小${_set.length}');
}
/// 获取详细的性能报告
PerformanceReport getReport() {
return PerformanceReport(
setName: _name,
operationCount: _operationCount,
totalTime: _totalTime,
currentSize: _set.length,
averageTime: _totalTime / _operationCount,
);
}
}
/// 性能报告类
class PerformanceReport {
final String setName;
final int operationCount;
final int totalTime;
final int currentSize;
final double averageTime;
PerformanceReport({
required this.setName,
required this.operationCount,
required this.totalTime,
required this.currentSize,
required this.averageTime,
});
void printDetailedReport() {
print('=== $setName 详细性能报告 ===');
print('总操作次数: $operationCount');
print('总耗时: ${totalTime}μs');
print('平均操作时间: ${averageTime.toStringAsFixed(2)}μs');
print('当前Set大小: $currentSize');
print('操作效率: ${(operationCount / (totalTime / 1000000)).toStringAsFixed(0)} ops/sec');
// 性能等级评估
String performanceGrade = _getPerformanceGrade();
print('性能等级: $performanceGrade');
}
String _getPerformanceGrade() {
if (averageTime < 1) return 'A+ (优秀)';
if (averageTime < 5) return 'A (良好)';
if (averageTime < 10) return 'B (一般)';
if (averageTime < 50) return 'C (较差)';
return 'D (需要优化)';
}
}
6. 性能问题诊断工具
/// 性能问题诊断工具
class SetPerformanceDiagnostic<T> {
/// 诊断Set性能问题
static DiagnosticReport<T> diagnose<T>(Set<T> set, List<T> sampleData) {
DiagnosticReport<T> report = DiagnosticReport<T>(set);
// 检查Set类型
report.setType = set.runtimeType.toString();
// 检查大小
report.currentSize = set.length;
// 测试插入性能
report.insertPerformance = _testInsertPerformance(set, sampleData.take(100).toList());
// 测试查找性能
report.lookupPerformance = _testLookupPerformance(set, sampleData.take(100).toList());
// 检查哈希分布(如果是HashSet)
if (set is Set<T> && T == String) {
report.hashDistribution = _analyzeHashDistribution(set.cast<String>());
}
// 生成建议
report.recommendations = _generateRecommendations(report);
return report;
}
static double _testInsertPerformance<T>(Set<T> set, List<T> testData) {
Set<T> testSet = set.runtimeType == HashSet ? <T>{} :
set.runtimeType.toString().contains('LinkedHashSet') ? LinkedHashSet<T>() :
SplayTreeSet<T>();
Stopwatch sw = Stopwatch()..start();
for (T item in testData) {
testSet.add(item);
}
sw.stop();
return sw.elapsedMicroseconds / testData.length;
}
static double _testLookupPerformance<T>(Set<T> set, List<T> testData) {
Stopwatch sw = Stopwatch()..start();
for (T item in testData) {
set.contains(item);
}
sw.stop();
return sw.elapsedMicroseconds / testData.length;
}
static double _analyzeHashDistribution(Set<String> stringSet) {
if (stringSet.isEmpty) return 1.0;
Map<int, int> hashBuckets = {};
for (String item in stringSet) {
int bucket = item.hashCode % 1000; // 简化的桶分析
hashBuckets[bucket] = (hashBuckets[bucket] ?? 0) + 1;
}
// 计算分布均匀度(标准差)
double mean = stringSet.length / hashBuckets.length;
double variance = hashBuckets.values
.map((count) => (count - mean) * (count - mean))
.reduce((a, b) => a + b) / hashBuckets.length;
return variance / mean; // 变异系数
}
static List<String> _generateRecommendations<T>(DiagnosticReport<T> report) {
List<String> recommendations = [];
// 基于Set类型的建议
if (report.setType.contains('SplayTree') && report.currentSize > 10000) {
recommendations.add('考虑使用HashSet以获得更好的查找性能');
}
if (report.setType.contains('LinkedHashSet') && report.currentSize > 50000) {
recommendations.add('如果不需要保持插入顺序,考虑使用HashSet');
}
// 基于性能的建议
if (report.insertPerformance > 10) {
recommendations.add('插入性能较差,检查哈希函数实现或考虑批量操作');
}
if (report.lookupPerformance > 5) {
recommendations.add('查找性能较差,可能存在哈希冲突问题');
}
// 基于哈希分布的建议
if (report.hashDistribution != null && report.hashDistribution! > 2.0) {
recommendations.add('哈希分布不均匀,建议优化hashCode实现');
}
if (report.currentSize > 100000) {
recommendations.add('大型Set建议实施内存管理策略,如定期清理或分页处理');
}
if (recommendations.isEmpty) {
recommendations.add('当前Set性能良好,无需特别优化');
}
return recommendations;
}
}
/// 诊断报告类
class DiagnosticReport<T> {
final Set<T> targetSet;
String setType = '';
int currentSize = 0;
double insertPerformance = 0;
double lookupPerformance = 0;
double? hashDistribution;
List<String> recommendations = [];
DiagnosticReport(this.targetSet);
void printReport() {
print('=== Set性能诊断报告 ===');
print('Set类型: $setType');
print('当前大小: $currentSize');
print('插入性能: ${insertPerformance.toStringAsFixed(2)}μs/op');
print('查找性能: ${lookupPerformance.toStringAsFixed(2)}μs/op');
if (hashDistribution != null) {
print('哈希分布均匀度: ${hashDistribution!.toStringAsFixed(2)} (越小越好)');
}
print('\n优化建议:');
for (int i = 0; i < recommendations.length; i++) {
print('${i + 1}. ${recommendations[i]}');
}
print();
}
}
使用性能分析工具的完整示例
/// 完整的性能分析示例
void performanceAnalysisExample() {
print('=== Set性能分析完整示例 ===\n');
// 1. 基准测试
print('1. 运行基准测试...');
runSetBenchmarkSuite();
// 2. 内存监控
print('2. 内存使用监控...');
Set<String> testSet = <String>{};
MemoryMonitor.monitorSetMemory(testSet, 'TestStringSet');
// 3. 实时监控
print('3. 实时性能监控...');
RealTimeSetMonitor<int> monitor = RealTimeSetMonitor(<int>{}, 'RealTimeTest');
for (int i = 0; i < 5000; i++) {
monitor.add(i);
if (i % 2 == 0) monitor.contains(i ~/ 2);
if (i % 3 == 0) monitor.remove(i ~/ 3);
}
PerformanceReport report = monitor.getReport();
report.printDetailedReport();
// 4. 性能诊断
print('4. 性能问题诊断...');
Set<String> diagnosisSet = {'test1', 'test2', 'test3'};
List<String> sampleData = List.generate(1000, (i) => 'item_$i');
DiagnosticReport<String> diagnosticReport =
SetPerformanceDiagnostic.diagnose(diagnosisSet, sampleData);
diagnosticReport.printReport();
print('性能分析完成!');
}
通过这些性能分析和调试工具,开发者可以:
- 识别性能瓶颈:通过基准测试找出最慢的操作
- 监控内存使用:防止内存泄漏和过度消耗
- 实时性能跟踪:在开发过程中持续监控性能
- 性能回归检测:确保代码修改不会导致性能退化
- 问题诊断:自动分析性能问题并提供优化建议
常见陷阱和错误
1. 空大括号陷阱
// ❌ 这创建的是Map,不是Set!
var wrong = {}; // Map<dynamic, dynamic>
print(wrong.runtimeType); // _Map<dynamic, dynamic>
// ✅ 正确的空Set创建方式
var correctSet = <String>{};
var anotherSet = Set<String>();
print(correctSet.runtimeType); // _Set<String>
2. 修改不可变Set
Set<int> immutableSet = Set.unmodifiable([1, 2, 3]);
// ❌ 试图修改不可变Set
try {
immutableSet.add(4); // UnsupportedError
immutableSet.remove(1); // UnsupportedError
immutableSet.clear(); // UnsupportedError
} catch (e) {
print('不可变Set错误: $e');
}
// ✅ 检查Set是否可修改
extension SetExtensions<T> on Set<T> {
bool get isModifiable {
try {
add(first);
remove(first);
return true;
} catch (e) {
return false;
}
}
}
3. 自定义对象的哈希问题
class BadPerson {
String name;
int age;
BadPerson(this.name, this.age);
// ❌ 只重写了==,没有重写hashCode
@override
bool operator ==(Object other) =>
other is BadPerson && other.name == name;
@override
String toString() => 'BadPerson($name, $age)';
}
class GoodPerson {
String name;
int age;
GoodPerson(this.name, this.age);
// ✅ 同时重写==和hashCode
@override
bool operator ==(Object other) =>
other is GoodPerson && other.name == name;
@override
int get hashCode => name.hashCode;
@override
String toString() => 'GoodPerson($name, $age)';
}
// 测试
Set<BadPerson> badSet = {BadPerson('Alice', 25), BadPerson('Alice', 30)};
Set<GoodPerson> goodSet = {GoodPerson('Alice', 25), GoodPerson('Alice', 30)};
print('Bad set length: ${badSet.length}'); // 可能是2(错误)
print('Good set length: ${goodSet.length}'); // 1(正确)
4. 集合运算的类型问题
Set<int> integers = {1, 2, 3};
Set<double> doubles = {2.0, 3.0, 4.0};
// ❌ 类型不匹配的运算
// Set<int> result = integers.union(doubles); // 编译错误
// ✅ 正确的处理方式
Set<num> numResult = integers.cast<num>().union(doubles.cast<num>());
print(numResult); // {1, 2, 3, 2.0, 3.0, 4.0} 注意:2和2.0是不同的
// ✅ 或者转换为相同类型
Set<int> intResult = integers.union(doubles.map((d) => d.toInt()).toSet());
print(intResult); // {1, 2, 3, 4}
5. 迭代时修改Set
Set<int> numbers = {1, 2, 3, 4, 5};
// ❌ 在遍历时修改Set会抛出异常
try {
for (int number in numbers) {
if (number % 2 == 0) {
numbers.remove(number); // ConcurrentModificationError!
}
}
} catch (e) {
print('并发修改错误: $e');
}
// ✅ 正确的方式:使用removeWhere
numbers.removeWhere((number) => number % 2 == 0);
// ✅ 或者先收集要删除的元素
Set<int> toRemove = numbers.where((n) => n % 2 == 0).toSet();
numbers.removeAll(toRemove);
6. Set顺序的误解
Set<int> numbers = {3, 1, 4, 1, 5, 9};
// ❌ 不要依赖Set的顺序(HashSet是无序的)
// print('First: ${numbers.first}'); // 结果不确定
// ✅ 如果需要有序,使用LinkedHashSet或排序
import 'dart:collection';
LinkedHashSet<int> orderedSet = LinkedHashSet.from([3, 1, 4, 1, 5, 9]);
print('Ordered first: ${orderedSet.first}'); // 3
List<int> sortedList = numbers.toList()..sort();
print('Sorted: $sortedList'); // [1, 3, 4, 5, 9]
7. 类型转换的安全性
Set<dynamic> mixed = {1, 'hello', 2.5, null};
// ❌ 不安全的类型转换
try {
Set<int> integers = mixed.map((e) => e as int).toSet(); // 运行时错误!
} catch (e) {
print('类型转换错误: $e');
}
// ✅ 安全的类型过滤和转换
Set<int> safeIntegers = mixed.whereType<int>().toSet();
print(safeIntegers); // {1}
// ✅ 带默认值的转换
Set<int> withDefaults = mixed.map((e) {
if (e is int) return e;
if (e is String) return int.tryParse(e) ?? 0;
if (e is double) return e.toInt();
return 0;
}).toSet();
最佳实践
1. 选择合适的Set实现
// ✅ 根据需求选择Set类型
import 'dart:collection';
// 需要最快的查找速度
Set<String> fastLookup = <String>{};
// 需要保持插入顺序
LinkedHashSet<String> ordered = LinkedHashSet<String>();
// 需要自动排序
SplayTreeSet<String> sorted = SplayTreeSet<String>();
// 需要自定义排序
SplayTreeSet<String> customSorted = SplayTreeSet<String>(
(a, b) => a.length.compareTo(b.length)
);
2. 正确实现自定义对象
// ✅ 自定义类的最佳实践
class Person {
final String name;
final int age;
const Person(this.name, this.age);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name &&
age == other.age;
@override
int get hashCode => Object.hash(name, age);
@override
String toString() => 'Person($name, $age)';
}
// 使用
Set<Person> people = {
Person('Alice', 25),
Person('Bob', 30),
Person('Alice', 25), // 会被自动去重
};
3. 高效的集合操作
// ✅ 使用集合运算而非循环
Set<int> set1 = {1, 2, 3, 4, 5};
Set<int> set2 = {4, 5, 6, 7, 8};
// 高效的方式
Set<int> common = set1.intersection(set2);
Set<int> combined = set1.union(set2);
Set<int> unique = set1.difference(set2);
// ✅ 批量操作
Set<int> target = <int>{};
List<int> source = [1, 2, 3, 4, 5];
target.addAll(source); // 比循环add更高效
4. 安全的类型处理
// ✅ 类型安全的Set操作
Set<dynamic> mixed = {1, 'hello', 2.5, true};
// 类型过滤
Set<int> integers = mixed.whereType<int>().toSet();
Set<String> strings = mixed.whereType<String>().toSet();
// 安全转换
Set<String> allStrings = mixed.map((e) => e.toString()).toSet();
5. 内存管理
// ✅ 及时清理大型Set
class CacheManager {
final Set<String> _cache = <String>{};
static const int maxSize = 1000;
void addToCache(String item) {
_cache.add(item);
// 限制缓存大小
if (_cache.length > maxSize) {
// 可以实现LRU策略或简单清理
_cache.clear(); // 简单清理
_cache.add(item);
}
}
void cleanup() {
_cache.clear();
}
}
6. 空安全处理
// ✅ 安全的Set操作
Set<String>? nullableSet;
// 安全访问
if (nullableSet?.isNotEmpty ?? false) {
print(nullableSet!.first);
}
// 使用null-aware操作符
int length = nullableSet?.length ?? 0;
bool hasItems = nullableSet?.isNotEmpty ?? false;
// 安全的查找
String? found = nullableSet?.lookup('target');
总结
Dart的Set类提供了强大而高效的无重复元素集合操作。在使用时应该注意:
- 选择合适的实现:根据需求选择HashSet、LinkedHashSet或SplayTreeSet
- 注意性能影响:了解各种操作的时间复杂度,选择高效的算法
- 保证类型安全:使用泛型确保类型安全,正确实现自定义对象的相等性
- 处理边界情况:检查空Set情况和异常处理
- 合理使用集合运算:union、intersection、difference等方法可以让代码更简洁高效
- 避免常见陷阱:注意空大括号语法、不可变Set限制、并发修改等问题
通过掌握这些API和最佳实践,可以更高效地使用Dart中的Set类型,编写出高质量的代码。
本文档基于Dart 3.x版本整理,如有更新请参考官方文档。