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
類別還有其他重要方法,例如map
、where
、expand
、contains
、forEach
、reduce
和fold
。這些方法可以用來處理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)
迭代器是一種可以逐一存取集合元素的物件。迭代器可以用來存取列表、集合和對映的元素。迭代器的優點是可以懶惰地存取元素,不需要一次性地載入所有元素。
選擇集合型別
根據不同的需求,可以選擇不同的集合型別。例如,如果需要按照元素的順序存取,則可以使用列表。如果需要快速地檢查元素是否存在於集合中,則可以使用集合。如果需要透過鍵來存取元素,則可以使用對映。
挑戰
以下是一些挑戰,目的是測試您對迭代器的瞭解:
- 使用
myList.iterator
來存取列表的迭代器。 - 手動地透過
moveNext
和current
來存取列表的元素。 - 建立一個自定義的迭代器集合,包含所有的費伯納西數(Fibonacci numbers)。
關鍵點
- 列表是一種有序的集合。
- 集合是一種無序的集合。
- 對映是一種鍵值對的集合。
- 迭代器是一種可以逐一存取集合元素的物件。
- 根據不同的需求,可以選擇不同的集合型別。
從效能評估的視角來看,Dart 的 Iterable 提供了處理大量資料的有效機制,尤其在記憶體管理方面展現優勢。透過同步生成器 sync*
與 yield
關鍵字,Iterable 可以按需產生元素,避免一次性載入所有資料至記憶體,這對於處理大型檔案或串流資料至關重要。然而,Iterable 的惰性求值特性也意味著每次取值都需要重新計算,在需要頻繁隨機存取元素的場景下,效能表現可能不如 List 或 Set。
深入分析 Iterable 的特性,可以發現其核心價值在於平衡記憶體消耗與計算成本。雖然 List 提供了快速的隨機存取,但在大資料場景下,其記憶體佔用可能成為瓶頸。Iterable 則提供了一種以時間換空間的策略,透過惰性求值來降低記憶體壓力。此外,Iterable 提供的 map
、where
、reduce
等方法,也方便開發者以函數語言程式設計的方式處理資料串流,提升程式碼的可讀性和 maintainability。
展望未來,隨著資料量的持續增長,Iterable 的價值將更加凸顯。預計 Dart 語言會持續強化 Iterable 的功能,例如提供更豐富的非同步處理能力,以更好地適應現代應用開發的需求。同時,結合非同步程式設計模型,Iterable 將在處理網路請求、檔案讀寫等 I/O 密集型任務中扮演更重要的角色。
對於需要處理大量資料的 Dart 開發者而言,深入理解並善用 Iterable 是提升應用程式效能的關鍵。建議開發者根據實際應用場景,權衡記憶體消耗與計算成本,選擇最合適的資料結構。在處理大型資料集時,優先考慮使用 Iterable,並結合函數語言程式設計技巧,以達到最佳的效能和程式碼品質。