ZetCode

Dart HashSet

最后修改于 2025 年 4 月 4 日

在 Dart 中,HashSet 是一个包含唯一元素的无序集合。它使用哈希表提供快速的查找、添加和删除操作。

HashSet 实现了 Set 接口,并要求元素具有一致的 Object.==Object.hashCode 实现。不允许重复元素。

创建 HashSet

创建 HashSet 的最简单方法是使用构造函数。我们可以指定元素类型和可选的相等比较器。

main.dart
import 'dart:collection';

void main() {
  var colors = HashSet<String>();
  colors.add('red');
  colors.add('green');
  colors.add('blue');
  colors.add('red'); // Duplicate ignored

  print(colors);
}

我们创建一个颜色 HashSet。泛型类型指定了 String 元素。我们使用 add() 添加元素,重复项会被自动忽略。

$ dart main.dart
{red, green, blue}

从 Iterable 创建 HashSet

我们可以使用 HashSet.from 构造函数从现有的 Iterable 创建 HashSet。这会自动删除重复项。

main.dart
import 'dart:collection';

void main() {
  var numbers = [1, 2, 3, 2, 4, 1, 5];
  var uniqueNumbers = HashSet.from(numbers);

  print('Original: $numbers');
  print('Unique: $uniqueNumbers');
}

这会从一个包含重复项的列表中创建一个 HashSet。结果 HashSet 只包含原始列表中的唯一值。

$ dart main.dart
Original: [1, 2, 3, 2, 4, 1, 5]
Unique: {1, 2, 3, 4, 5}

检查内容

HashSet 提供了检查元素是否存在和集合状态的方法。这些操作通常具有 O(1) 的复杂度。

main.dart
import 'dart:collection';

void main() {
  var primes = HashSet.from([2, 3, 5, 7, 11]);

  print(primes.contains(5));    // true
  print(primes.contains(4));    // false
  print(primes.isEmpty);       // false
  print(primes.length);        // 5
  print(primes.first);         // 2 (arbitrary)
}

我们检查 HashSet 的各种属性。第一个元素是任意的,因为 HashSet 不维护插入顺序。

$ dart main.dart
true
false
false
5
2

集合操作

HashSet 支持标准的集合操作,如联合、交集和差集。这些方法返回新的集合,而不会修改原始集合。

main.dart
import 'dart:collection';

void main() {
  var setA = HashSet.from([1, 2, 3, 4]);
  var setB = HashSet.from([3, 4, 5, 6]);

  print('Union: ${setA.union(setB)}');
  print('Intersection: ${setA.intersection(setB)}');
  print('Difference: ${setA.difference(setB)}');
}

我们演示了三种基本的集合操作。每种操作都会创建一个包含该操作结果的新 HashSet。

$ dart main.dart
Union: {1, 2, 3, 4, 5, 6}
Intersection: {3, 4}
Difference: {1, 2}

遍历 HashSet

我们可以使用各种方法遍历 HashSet 元素。迭代顺序是未定义的,但对于同一个集合是稳定的。

main.dart
import 'dart:collection';

void main() {
  var languages = HashSet.from(['Dart', 'Java', 'Python', 'Go']);

  print('For loop:');
  for (var lang in languages) {
    print(lang);
  }

  print('\nForEach:');
  languages.forEach((lang) => print(lang));

  print('\nIterator:');
  var it = languages.iterator;
  while (it.moveNext()) {
    print(it.current);
  }
}

这演示了遍历 HashSet 的三种方法。对于给定的集合,所有方法都将以相同的(任意)顺序处理元素。

$ dart main.dart
For loop:
Dart
Java
Python
Go

ForEach:
Dart
Java
Python
Go

Iterator:
Dart
Java
Python
Go

移除元素

HashSet 提供了单独删除元素或清空整个集合的方法。这些操作通常具有 O(1) 的复杂度。

main.dart
import 'dart:collection';

void main() {
  var fruits = HashSet.from(['apple', 'banana', 'orange', 'kiwi']);

  print('Original: $fruits');

  fruits.remove('banana');
  print('After remove: $fruits');

  fruits.removeWhere((fruit) => fruit.length > 5);
  print('After removeWhere: $fruits');

  fruits.clear();
  print('After clear: $fruits');
}

我们演示了元素移除方法。removeWhere 允许根据条件过滤元素,而 clear 则清空整个集合。

$ dart main.dart
Original: {apple, banana, orange, kiwi}
After remove: {apple, orange, kiwi}
After removeWhere: {apple, kiwi}
After clear: {}

自定义对象的 HashSet

在 HashSet 中使用自定义对象时,我们必须正确实现 hashCode 和 ==。这可确保唯一性和查找的正确行为。

main.dart
import 'dart:collection';

class Book {
  final String title;
  final String author;

  Book(this.title, this.author);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Book && title == other.title && author == other.author;

  @override
  int get hashCode => title.hashCode ^ author.hashCode;
}

void main() {
  var library = HashSet<Book>();
  var book1 = Book('Dart in Depth', 'John Doe');
  var book2 = Book('Flutter Basics', 'Jane Smith');
  var book3 = Book('Dart in Depth', 'John Doe'); // Same as book1

  library.add(book1);
  library.add(book2);
  library.add(book3);

  print('Library size: ${library.length}');
  print('book1 and book3 same: ${library.contains(book1) == library.contains(book3)}');
}

Book 类实现了 == 和 hashCode 以确保逻辑相等。book1 和 book3 被视为相同,因此只添加一个到集合中。

$ dart main.dart
Library size: 2
book1 and book3 same: true

最佳实践

来源

Dart HashSet 文档

本教程介绍了 Dart 的 HashSet,并通过实际示例演示了其无序唯一集合的关键特性和用法模式。

作者

我的名字是 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 Dart 教程