ZetCode

Dart Iterator

最后修改日期:2025 年 6 月 7 日

在 Dart 中,Iterator 接口作为一种基本的机制,用于按顺序访问集合中的元素。它在 Dart 的迭代系统中起着至关重要的作用,允许开发人员高效地遍历列表、集合、映射和其他可迭代结构。

Iterator 接口定义了两个基本方法:

在 Dart 中,大多数集合都实现了 Iterable 接口,该接口通过 iterator 属性提供迭代器。这允许使用 for-in 循环、forEach() 方法或通过手动管理迭代器(使用 moveNext()current)轻松地迭代元素。

Iterator 接口在处理异步或自定义集合时特别有用,在这些情况下,显式控制迭代可以优化性能和内存使用。通过实现 Iterator,开发人员可以为专用数据结构创建定制的遍历逻辑。

基本 Iterator 用法

此示例展示了手动使用迭代器的基本方法。

main.dart
void main() {
  var numbers = [1, 2, 3, 4, 5];
  var iterator = numbers.iterator;
  
  while (iterator.moveNext()) {
    print(iterator.current);
  }
}

我们从列表中创建一个迭代器,并使用 moveNext() 来遍历元素。current 属性获取当前元素。这就是 for-in 循环的内部工作原理。

$ dart main.dart
1
2
3
4
5

自定义 Iterator 实现

此示例演示了创建一个实现 Iterator 的自定义类。

main.dart
class Countdown implements Iterator<int> {
  int _current = 5;
  
  @override
  int get current => _current;
  
  @override
  bool moveNext() {
    if (_current > 0) {
      _current--;
      return true;
    }
    return false;
  }
}

void main() {
  var countdown = Countdown();
  
  while (countdown.moveNext()) {
    print(countdown.current);
  }
}

我们用从 5 到 0 的倒计时实现了 Iterator 接口。moveNext() 会递减 _current 直到它达到 0。current getter 返回该值。

$ dart main.dart
4
3
2
1
0

带 Iterator 的 Iterable

此示例展示了如何创建一个提供迭代器的自定义 Iterable 类。

main.dart
class FibonacciSequence implements Iterable<int> {
  final int count;
  
  FibonacciSequence(this.count);
  
  @override
  Iterator<int> get iterator => _FibonacciIterator(count);
}

class _FibonacciIterator implements Iterator<int> {
  final int count;
  int _current = 0;
  int _next = 1;
  int _position = 0;
  
  _FibonacciIterator(this.count);
  
  @override
  int get current => _current;
  
  @override
  bool moveNext() {
    if (_position < count) {
      var newNext = _current + _next;
      _current = _next;
      _next = newNext;
      _position++;
      return true;
    }
    return false;
  }
}

void main() {
  var fib = FibonacciSequence(7);
  
  for (var num in fib) {
    print(num);
  }
}

我们创建了一个斐波那契数列生成器。Iterable 提供了一个生成数字的迭代器。这允许该类在 for-in 循环中使用。

$ dart main.dart
1
1
2
3
5
8
13

带复杂对象的 Iterator

此示例展示了如何将迭代器与自定义对象集合一起使用。

main.dart
import 'dart:core';

class Book {
  final String title;
  final String author;

  Book(this.title, this.author);

  @override
  String toString() => '$title by $author';
}

class Library extends Iterable<Book> {
  final List<Book> _books = [];

  void add(Book book) => _books.add(book);

  @override
  Iterator<Book> get iterator => _books.iterator;
}

void main() {
  var library = Library();
  library.add(Book('Dart in Action', 'Manning'));
  library.add(Book('Flutter in Action', 'Manning'));
  library.add(Book('Effective Dart', 'Google'));

  for (var book in library) {
    print(book);
  }
}

我们创建了一个包含 Books 并扩展了 Iterable 的 Library 类。迭代器来自底层的 List。这种模式在集合类中很常见。

$ dart main.dart
Dart in Action by Manning
Flutter in Action by Manning
Effective Dart by Google

在下一个示例中,我们将学习如何扩展 IterableMixin 接口来创建自定义的可迭代集合。

main.dart
import 'dart:collection';

class Book {
  final String title;
  final String author;
  
  Book(this.title, this.author);
  
  @override
  String toString() => '$title by $author';
}

class Library extends IterableMixin<Book> {
  final List<Book> _books = [];
  
  void add(Book book) => _books.add(book);
  
  @override
  Iterator<Book> get iterator => _books.iterator;
}

void main() {
  final library = Library()
    ..add(Book('Dart in Action', 'Manning'))
    ..add(Book('Flutter in Action', 'Manning'))
    ..add(Book('Effective Dart', 'Google'));

  // Using iterator directly
  final iterator = library.iterator;
  while (iterator.moveNext()) {
    print(iterator.current);
  }

  // Using Iterable methods
  print('\nBooks with "Action":');
  library
      .where((book) => book.title.contains('Action'))
      .forEach(print);

  print('\nBook titles:');
  library
      .map((book) => book.title.toUpperCase())
      .forEach(print);
}

我们创建了一个扩展了 IterableMixin 的 Library 类,该类提供了 Iterable 接口的默认实现。这使我们能够专注于实现 iterator 方法。该示例演示了如何直接使用迭代器,还展示了用于过滤和转换元素的常见 Iterable 方法,如 wheremap

下一个示例展示了如何在不依赖 IterableMixin 的情况下完全满足 Iterable 接口。

main.dart
class Book {
  final String title;
  final String author;

  Book(this.title, this.author);

  @override
  String toString() => '$title by $author';
}

class Library implements Iterable<Book> {
  final List<Book> _books = [];

  void add(Book book) => _books.add(book);


  @override
  bool any(bool Function(Book element) test) {
    for (var book in _books) {
      if (test(book)) return true;
    }
    return false;
  }

  @override
  Iterable<R> cast<R>() sync* {
    for (var book in _books) {
      yield book as R;
    }
  }

  @override
  bool contains(Object? element) => _books.contains(element);

  @override
  Book elementAt(int index) => _books.elementAt(index);

  @override
  bool every(bool Function(Book element) test) {
    for (var book in _books) {
      if (!test(book)) return false;
    }
    return true;
  }

  @override
  Iterable<T> expand<T>(Iterable<T> Function(Book element) f) sync* {
    for (var book in _books) {
      yield* f(book);
    }
  }

  @override
  Book get first => _books.first;

  @override
  Book firstWhere(bool Function(Book element) test, {Book Function()? orElse}) {
    for (var book in _books) {
      if (test(book)) return book;
    }
    if (orElse != null) return orElse();
    throw StateError('No element');
  }

  @override
  T fold<T>(T initialValue, T Function(T previousValue, Book element) combine) {
    var value = initialValue;
    for (var book in _books) {
      value = combine(value, book);
    }
    return value;
  }

  @override
  Iterable<Book> followedBy(Iterable<Book> other) sync* {
    yield* _books;
    yield* other;
  }

  @override
  void forEach(void Function(Book element) f) {
    for (var book in _books) {
      f(book);
    }
  }

  @override
  bool get isEmpty => _books.isEmpty;

  @override
  bool get isNotEmpty => _books.isNotEmpty;

  @override
  Iterator<Book> get iterator => _books.iterator;

  @override
  String join([String separator = '']) => _books.join(separator);

  @override
  Book get last => _books.last;

  @override
  Book lastWhere(bool Function(Book element) test, {Book Function()? orElse}) {
    for (var i = _books.length - 1; i >= 0; i--) {
      if (test(_books[i])) return _books[i];
    }
    if (orElse != null) return orElse();
    throw StateError('No element');
  }

  @override
  int get length => _books.length;

  @override
  Iterable<T> map<T>(T Function(Book e) f) sync* {
    for (var book in _books) {
      yield f(book);
    }
  }

  @override
  Book reduce(Book Function(Book value, Book element) combine) {
    if (_books.isEmpty) throw StateError('No elements');
    var value = _books.first;
    for (var i = 1; i < _books.length; i++) {
      value = combine(value, _books[i]);
    }
    return value;
  }

  @override
  Book get single {
    if (_books.length == 1) return _books.first;
    if (_books.isEmpty) throw StateError('No elements');
    throw StateError('More than one element');
  }

  @override
  Book singleWhere(bool Function(Book element) test, {Book Function()? orElse}) {
    Book? result;
    var found = false;
    for (var book in _books) {
      if (test(book)) {
        if (found) throw StateError('More than one element');
        result = book;
        found = true;
      }
    }
    if (!found) {
      if (orElse != null) return orElse();
      throw StateError('No element');
    }
    return result!;
  }

  @override
  Iterable<Book> skip(int count) sync* {
    for (var i = count; i < _books.length; i++) {
      yield _books[i];
    }
  }

  @override
  Iterable<Book> skipWhile(bool Function(Book value) test) sync* {
    var skipping = true;
    for (var book in _books) {
      if (skipping && test(book)) {
        continue;
      } else {
        skipping = false;
        yield book;
      }
    }
  }

  @override
  Iterable<Book> take(int count) sync* {
    var taken = 0;
    for (var book in _books) {
      if (taken++ >= count) break;
      yield book;
    }
  }

  @override
  Iterable<Book> takeWhile(bool Function(Book value) test) sync* {
    for (var book in _books) {
      if (!test(book)) break;
      yield book;
    }
  }

  @override
  List<Book> toList({bool growable = true}) => List<Book>.from(_books, growable: growable);

  @override
  Set<Book> toSet() => Set<Book>.from(_books);

  @override
  Iterable<Book> where(bool Function(Book element) test) sync* {
    for (var book in _books) {
      if (test(book)) yield book;
    }
  }

  @override
  Iterable<T> whereType<T>() sync* {
    for (var book in _books) {
      if (book is T) yield book as T;
    }
  }
}

void main() {
  var library = Library();
  library.add(Book('Dart in Action', 'Manning'));
  library.add(Book('Flutter in Action', 'Manning'));
  library.add(Book('Effective Dart', 'Google'));

  for (var book in library) {
    print(book);
  }

  print('\nBooks with "Action":');
  for (var book in library.where((b) => b.title.contains('Action'))) {
    print(book);
  }
}

所有必需的 Iterable 方法都已手动实现。这允许 Library 类像 Dart 中的任何其他可迭代集合一样使用。该示例演示了如何遍历库并根据条件过滤书籍。这种方法提供了对迭代过程的完全控制,并允许自定义行为,同时仍然遵守 Iterable 接口约定。当您需要创建一种自定义集合类型,该类型表现得像标准可迭代对象,但具有特定的要求或优化时,此方法非常有用。

Iterator 工具

此示例演示了 takeWhile 和 skipWhile 等有用的迭代器工具。

main.dart
void main() {
  var numbers = [1, 3, 5, 2, 4, 6, 8, 7];
  
  print('Numbers while odd:');
  var oddIterator = numbers.takeWhile((n) => n.isOdd).iterator;
  while (oddIterator.moveNext()) {
    print(oddIterator.current);
  }
  
  print('\nNumbers after first even:');
  var afterEven = numbers.skipWhile((n) => n.isOdd).iterator;
  while (afterEven.moveNext()) {
    print(afterEven.current);
  }
}

我们使用 takeWhile 来获取条件失败之前的元素,并使用 skipWhile 来跳过条件失败之前的元素。这些方法返回新的迭代器。

$ dart main.dart
Numbers while odd:
1
3
5

Numbers after first even:
2
4
6
8
7

来源

Dart Iterator 文档

本教程通过实际示例介绍了 Dart 的 Iterator 接口,展示了基本用法、自定义实现和常见模式。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 Dart 教程