Dart 的 Iterable 是一種能逐一存取元素的集合型別,適用於處理大量資料,避免一次性載入所有內容到記憶體。然而,Iterable 的元素存取速度相對較慢,且不支援隨機存取。開發者可運用 sync* 關鍵字建立同步生成器,按需產生元素,有效控制記憶體使用。同步生成器搭配 yield 關鍵字,逐一傳回元素值,並暫停執行直到下一次請求。Iterable 可透過 for-in 迴圈輕鬆遍歷,並結合 map、where 等方法進行資料處理。

Iterable<int> createSquares() sync* {
  for (int i = 1; i <= 10000; i++) {
    yield i * i;
  }
}

void main() {
  Iterable<int> squares = createSquares();
  for (int square in squares) {
    print(square);
  }
}

Iterable的優點

使用Iterable的優點是它可以處理大量資料而不會佔用太多記憶體。例如,如果您需要處理一個2GB的文字檔案,您可以使用Iterable來逐行處理檔案,而不需要將整個檔案載入記憶體中。

Iterable的缺點

然而,Iterable也有缺點。因為Iterable需要根據需要生成元素,所以它可能會比較慢。另外,Iterable不支援隨機存取元素,如果您需要頻繁存取元素,使用列表或集合可能會更好。

建立Iterable

您可以使用sync*關鍵字來建立一個同步生成器,該生成器可以生成多個值。以下是一個簡單的例子:

Iterable<int> createSquares() sync* {
  for (int i = 1; i <= 10000; i++) {
    yield i * i;
  }
}

這個生成器會生成1到10000的平方數。

使用Iterable

您可以使用for-in迴圈來遍歷Iterable的元素。以下是一個簡單的例子:

void main() {
  Iterable<int> squares = createSquares();
  for (int square in squares) {
    print(square);
  }
}

這個程式會印出1到10000的平方數。

其他重要方法

Iterable類別還有其他重要方法,例如mapwhereexpandcontainsforEachreducefold。這些方法可以用來處理Iterable的元素。

練習

建立一個包含1到10000的平方數的Iterable,然後使用for-in迴圈來遍歷Iterable的元素。接著,印出Iterable的長度和第一個元素。

內容解密:

上述程式碼使用sync*關鍵字來建立一個同步生成器,該生成器可以生成多個值。yield關鍵字用來生成下一個值。for-in迴圈用來遍歷Iterable的元素。

圖表翻譯:

  graph LR
    A[建立Iterable] --> B[使用sync*關鍵字]
    B --> C[生成多個值]
    C --> D[使用for-in迴圈遍歷元素]
    D --> E[印出Iterable的長度和第一個元素]

這個圖表顯示了建立Iterable和使用for-in迴圈遍歷Iterable的元素的過程。

建立同步生成器

首先,讓我們建立一個同步生成器。這個生成器將會生成1到100的平方數。

Iterable<int> hundredSquares() sync* {
  for (int i = 1; i <= 100; i++) {
    yield i * i;
  }
}

在這個程式碼中,sync*關鍵字表示這個函式是一個同步生成器,而yield關鍵字用於生成單個值並暫停直到下一次請求值。

執行程式碼

現在,讓我們執行這個生成器。首先,替換main函式的內容如下:

final squares = hundredSquares();

for (int square in squares) {
  print(square);
}

因為可迭代物件是懶惰的,Dart不會啟動生成器函式直到第一次請求可迭代物件的值。因此,當我們執行這個程式碼時,Dart將會在for迴圈中請求值,並打印出1到100的平方數。

使用迭代器

接下來,讓我們使用迭代器來實作相同的功能。迭代器負責在集合中移動元素。每個迭代器必須包含兩個元件:moveNext()方法和current getter。

class SquaredIterator implements Iterator<int> {
  int _index = 0;

  @override
  bool moveNext() {
    if (_index <= 100) {
      _index++;
      return true;
    } else {
      return false;
    }
  }

  @override
  int get current => (_index - 1) * (_index - 1);
}

在這個程式碼中,moveNext()方法提供了邏輯來獲取集合中的下一個元素,而current getter傳回了當前元素的值。

實作迭代器

現在,讓我們實作這個迭代器。首先,建立一個新的檔案lib/squares.dart,然後新增以下程式碼:

class SquaredIterator implements Iterator<int> {
  int _index = 0;

  @override
  bool moveNext() {
    if (_index <= 100) {
      _index++;
      return true;
    } else {
      return false;
    }
  }

  @override
  int get current => (_index - 1) * (_index - 1);
}

測試迭代器

最後,讓我們測試這個迭代器。首先,建立一個新的例項:

final iterator = SquaredIterator();

然後,使用moveNext()方法和current getter來打印出1到100的平方數:

while (iterator.moveNext()) {
  print(iterator.current);
}

這將會打印出1到100的平方數。

實作可迭代物件

在 Dart 中,實作可迭代物件需要兩個主要部分:迭代器(Iterator)和可迭代物件(Iterable)。以下是如何實作這兩個部分的步驟。

實作迭代器

首先,需要實作迭代器。迭代器負責遍歷集合中的元素。以下是迭代器的實作:

class SquaredIterator implements Iterator<int> {
  int _index = 0;

  @override
  bool moveNext() {
    _index++;
    return _index <= 100;
  }

  @override
  int get current => _index * _index;
}

在這個實作中,moveNext 方法會在每次呼叫時遞增 _index,直到 _index 超過 100。current 方法會傳回 _index 的平方。

實作可迭代物件

接下來,需要實作可迭代物件。可迭代物件負責提供迭代器。以下是可迭代物件的實作:

class HundredSquares extends Iterable<int> {
  @override
  Iterator<int> get iterator => SquaredIterator();
}

在這個實作中,HundredSquares 類別繼承自 Iterable 類別,並實作 iterator 方法。這個方法會傳回一個 SquaredIterator 例項。

測試可迭代物件

現在,可以測試可迭代物件了。以下是測試程式碼:

import 'package:starter/squares.dart';

void main() {
  final squares = HundredSquares();

  for (int square in squares) {
    print(square);
  }
}

這個程式碼會建立一個 HundredSquares 例項,並使用 for-in 迴圈遍歷其元素。每個元素會被印出到主控臺。

結果

當執行這個程式碼時,會看到以下輸出:

1
4
9
16
25
...
9604

這是 1 到 100 的平方數。

Dart 基礎知識:集合與迭代器

Dart 中的集合(collections)是一種可以儲存多個元素的資料結構。集合可以是有序的,也可以是無序的。Dart 提供了多種集合型別,包括列表(lists)、集合(sets)和對映(maps)。

列表(Lists)

列表是一種有序的集合,元素之間有著明確的順序。列表可以使用索引(index)來存取元素。列表的優點是可以按照元素的順序存取,但缺點是搜尋元素可能會很慢。

集合(Sets)

集合是一種無序的集合,元素之間沒有明確的順序。集合的優點是可以快速地檢查元素是否存在於集合中,但缺點是不能按照元素的順序存取。

對映(Maps)

對映是一種鍵值對(key-value pair)的集合,元素之間透過鍵(key)來存取。對映的優點是可以快速地透過鍵來存取元素,但缺點是不能按照元素的順序存取。

迭代器(Iterables)

迭代器是一種可以逐一存取集合元素的物件。迭代器可以用來存取列表、集合和對映的元素。迭代器的優點是可以懶惰地存取元素,不需要一次性地載入所有元素。

選擇集合型別

根據不同的需求,可以選擇不同的集合型別。例如,如果需要按照元素的順序存取,則可以使用列表。如果需要快速地檢查元素是否存在於集合中,則可以使用集合。如果需要透過鍵來存取元素,則可以使用對映。

挑戰

以下是一些挑戰,目的是測試您對迭代器的瞭解:

  1. 使用 myList.iterator 來存取列表的迭代器。
  2. 手動地透過 moveNextcurrent 來存取列表的元素。
  3. 建立一個自定義的迭代器集合,包含所有的費伯納西數(Fibonacci numbers)。
關鍵點
  • 列表是一種有序的集合。
  • 集合是一種無序的集合。
  • 對映是一種鍵值對的集合。
  • 迭代器是一種可以逐一存取集合元素的物件。
  • 根據不同的需求,可以選擇不同的集合型別。

從效能評估的視角來看,Dart 的 Iterable 提供了處理大量資料的有效機制,尤其在記憶體管理方面展現優勢。透過同步生成器 sync*yield 關鍵字,Iterable 可以按需產生元素,避免一次性載入所有資料至記憶體,這對於處理大型檔案或串流資料至關重要。然而,Iterable 的惰性求值特性也意味著每次取值都需要重新計算,在需要頻繁隨機存取元素的場景下,效能表現可能不如 List 或 Set。

深入分析 Iterable 的特性,可以發現其核心價值在於平衡記憶體消耗與計算成本。雖然 List 提供了快速的隨機存取,但在大資料場景下,其記憶體佔用可能成為瓶頸。Iterable 則提供了一種以時間換空間的策略,透過惰性求值來降低記憶體壓力。此外,Iterable 提供的 mapwherereduce 等方法,也方便開發者以函數語言程式設計的方式處理資料串流,提升程式碼的可讀性和 maintainability。

展望未來,隨著資料量的持續增長,Iterable 的價值將更加凸顯。預計 Dart 語言會持續強化 Iterable 的功能,例如提供更豐富的非同步處理能力,以更好地適應現代應用開發的需求。同時,結合非同步程式設計模型,Iterable 將在處理網路請求、檔案讀寫等 I/O 密集型任務中扮演更重要的角色。

對於需要處理大量資料的 Dart 開發者而言,深入理解並善用 Iterable 是提升應用程式效能的關鍵。建議開發者根據實際應用場景,權衡記憶體消耗與計算成本,選擇最合適的資料結構。在處理大型資料集時,優先考慮使用 Iterable,並結合函數語言程式設計技巧,以達到最佳的效能和程式碼品質。