Dart 列表的 sort() 方法提供高效的排序功能,直接修改原始列表而非建立新列表。插入和刪除操作的效能取決於操作位置,在列表頭或中間操作可能觸發元素移動,影響效能。對於頻繁的插入刪除操作,尤其在列表頭部,連結串列是更優的選擇,其指標連線的結構特性使其更擅長此類操作。

列表是 Dart 中常用的資料結構,分為可變與不可變兩種。可變列表使用 varfinal 關鍵字宣告,並可透過 addremoveinsert 等方法修改內容。不可變列表則使用 const 關鍵字宣告,其內容一旦確定便無法更改,任何修改嘗試都會導致錯誤。List.unmodifiable 建構子則允許根據現有列表建立不可變版本。

Dart 也提供空安全特性以處理可空列表和列表中的可空元素。?. 運運算元允許安全地訪問成員,?? 運運算元則提供預設值以避免空值錯誤。集合(Set)是不允許重複元素的資料結構,常用於快速檢查元素是否存在。addremovecontainsunionintersectiondifference 等方法提供了集合的基本操作功能。

列表排序

Dart 中的列表可以使用 sort() 方法進行排序。這個方法會就地排序列表,意味著它不會建立一個新的列表,而是直接修改原始列表。

void main() {
  final integers = [32, 73, 2, 343, 7, 10, 1];
  integers.sort();
  print(integers); // [1, 2, 7, 10, 32, 73, 343]
}

在上面的例子中,integers 列表被排序後,元素按照從小到大的順序排列。

列表插入和刪除

當您需要在列表中插入或刪除元素時,Dart 提供了多種方法。然而,當您需要在列表的開始或中間插入或刪除元素時,Dart 需要內部移動元素,這可能會影響效能。

void main() {
  final list = [1, 2, 3, 4, 5];
  list.insert(0, 0); // 在列表開始插入元素
  print(list); // [0, 1, 2, 3, 4, 5]
  
  list.removeAt(0); // 刪除列表中的第一個元素
  print(list); // [1, 2, 3, 4, 5]
}

在上面的例子中,insert() 方法被用來在列表的開始插入一個元素,而 removeAt() 方法被用來刪除列表中的第一個元素。

使用連結串列

如果您需要在列表中頻繁插入或刪除元素,特別是在列表的開始或中間,使用連結串列(Linked List)可能是一個更好的選擇。連結串列是一種資料結構,它的元素之間透過指標連線,允許高效地插入和刪除元素。

內容解密:
  • sort() 方法就地排序列表,意味著它不會建立一個新的列表,而是直接修改原始列表。
  • 列表插入和刪除操作可能會影響效能,特別是在列表的開始或中間。
  • 連結串列是一種資料結構,它的元素之間透過指標連線,允許高效地插入和刪除元素。

圖表翻譯:

  flowchart TD
    A[列表] --> B[排序]
    B --> C[插入/刪除]
    C --> D[連結串列]
    D --> E[高效插入/刪除]

在上面的流程圖中,列表可以被排序,然後可以進行插入和刪除操作。如果需要高效地插入和刪除元素,連結串列是一個更好的選擇。

Dart 中的列表(List)

Dart 中的列表是一種集合,允許您儲存多個值。列表可以是可變的(mutable)或不可變的(immutable)。

建立列表

您可以使用 [] 符號建立一個列表。例如:

var fruits = ['apple', 'banana', 'orange'];

可變列表

可變列表可以使用 varfinal 關鍵字宣告。例如:

var desserts = ['cookies', 'cupcakes', 'donuts', 'pie'];
final desserts = ['cookies', 'cupcakes', 'donuts', 'pie'];

您可以使用 addremoveinsert 方法修改可變列表的內容。例如:

desserts.add('ice cream');
desserts.remove('cookies');
desserts.insert(0, 'cake');

不可變列表

不可變列表可以使用 const 關鍵字宣告。例如:

const desserts = ['cookies', 'cupcakes', 'donuts', 'pie'];

不可變列表的內容不能被修改。任何嘗試修改其內容的操作都會導致編譯錯誤。

列表方法

Dart 中的列表提供了多種方法,例如 sortindexOfremoveAt 等。例如:

var numbers = [4, 2, 7, 1];
numbers.sort();
print(numbers); // [1, 2, 4, 7]

var index = numbers.indexOf(2);
print(index); // 1

numbers.removeAt(1);
print(numbers); // [1, 4, 7]

練習

建立一個名為 months 的列表,並將 12 個月份的名稱新增到其中。然後,找到 “March” 的索引,並將其從列表中刪除。最後,將 “March” 插入到正確的位置,並列印列表的內容。

var months = [];
months.add('January');
months.add('February');
months.add('March');
months.add('April');
months.add('May');
months.add('June');
months.add('July');
months.add('August');
months.add('September');
months.add('October');
months.add('November');
months.add('December');

var index = months.indexOf('March');
months.removeAt(index);
months.insert(2, 'March');

print(months);

Dart 中的不可變列表

在 Dart 中,const 關鍵字可以用來宣告一個不可變的列表。例如:

const desserts = ['cookies', 'cupcakes', 'donuts', 'pie'];

由於 const 關鍵字的存在,該列表不能被修改。試圖新增、移除或更新列表中的元素將會導致執行時錯誤。

使用 const 列表字面量

如果你不能使用 const 關鍵字宣告變數本身,你可以使用 const 列表字面量來建立一個深度不可變的列表。例如:

final desserts = const ['cookies', 'cupcakes', 'donuts', 'pie'];

這種情況可能出現在提供類別中的預設值時。例如:

class Desserts {
  Desserts([this.desserts = const ['cookies']]);
  final List<String> desserts;
}

在這種情況下,dessertsfinal 的,但預設值是一個 const 列表字面量。這確保了預設列表的內容不能被修改。

使用 List.unmodifiable 建構子

如果你想要建立一個不可變的列表,但元素值在執行時才會知道,你可以使用 List.unmodifiable 建構子。例如:

final modifiableList = [DateTime.now(), DateTime.now()];
final unmodifiableList = List.unmodifiable(modifiableList);

在這種情況下,modifiableList 是一個可變的列表,但 unmodifiableList 是一個不可變的列表。

列表屬性

Dart 中的列表有幾個有用的屬性。以下是一個示例列表:

const drinks = ['water', 'milk', 'juice', 'soda'];

存取第一個和最後一個元素

你可以使用 firstlast 屬性存取列表中的第一個和最後一個元素。例如:

drinks.first // 'water'
drinks.last // 'soda'

這與使用索引存取元素等效:

drinks[0] // 'water'
drinks[drinks.length - 1] // 'soda'

檢查列表是否為空

你可以使用 isEmptyisNotEmpty 屬性檢查列表是否為空。例如:

drinks.isEmpty // false
drinks.isNotEmpty // true

這與以下程式碼等效:

drinks.length == 0 // false
drinks.length > 0 // true

圖表翻譯:

  graph LR
    A[列表] -->|first|> B[第一個元素]
    A -->|last|> C[最後一個元素]
    A -->|isEmpty|> D[是否為空]
    A -->|isNotEmpty|> E[是否不為空]

Lists 和 Dart

在 Dart 中,Lists 是一個非常重要的資料結構,允許你儲存和操作多個元素。這篇文章將介紹如何使用 Lists、迴圈和操作 Lists 的方法。

Lists 的基本操作

你可以使用 const 關鍵字宣告一個 List,並使用索引存取其元素。例如:

const desserts = ['cookies', 'cupcakes', 'donuts', 'pie'];
print(desserts[0]); // cookies

你也可以使用 length 屬性取得 List 的元素數量:

print(desserts.length); // 4

迴圈和 Lists

當你需要對 List 的每個元素進行操作時,迴圈是一個很好的工具。你可以使用 for 迴圈或 for-in 迴圈。

使用 for 迴圈

const desserts = ['cookies', 'cupcakes', 'donuts', 'pie'];
for (int i = 0; i < desserts.length; i++) {
  final item = desserts[i];
  print('I like $item.');
}

使用 for-in 迴圈

const desserts = ['cookies', 'cupcakes', 'donuts', 'pie'];
for (final item in desserts) {
  print('I also like $item!');
}

Lists 的操作

你可以使用 addAll 方法將另一個 List 的元素新增到目前的 List 中:

const pastries = ['cookies', 'croissants'];
const candy = ['Junior Mints', 'Twizzlers', 'M&Ms'];
final desserts = ['donuts'];
desserts.addAll(pastries);

你也可以使用 spread operator (...) 將兩個 List 合併:

const pastries = ['cookies', 'croissants'];
const candy = ['Junior Mints', 'Twizzlers', 'M&Ms'];
final desserts = ['donuts', ...pastries, ...candy];

練習

  1. 將以下 List 中的每個數字的平方輸出:
const numbers = [1, 2, 4, 7];

使用 for 迴圈和 for-in 迴圈分別解決這個問題。

  1. 使用 spread operator 將兩個 List 合併:
const pastries = ['cookies', 'croissants'];
const candy = ['Junior Mints', 'Twizzlers', 'M&Ms'];

Dart 中的列表操作

Dart 提供了多種方式來操作列表,包括新增元素、合併列表和條件式包含元素。

新增元素

可以使用 addAll 方法將一個列表的元素新增到另一個列表中。例如:

void main() {
  List<String> desserts = ['donuts'];
  List<String> pastries = ['cookies', 'croissants'];
  List<String> candy = ['Junior Mints', 'Twizzlers', 'M&Ms'];

  desserts.addAll(pastries);
  desserts.addAll(candy);

  print(desserts);
}

這將輸出:[donuts, cookies, croissants, Junior Mints, Twizzlers, M&Ms]

合併列表

Dart 也提供了 spread operator (...) 來合併列表。例如:

void main() {
  List<String> desserts = ['donuts', ...['cookies', 'croissants'], ...['Junior Mints', 'Twizzlers', 'M&Ms']];

  print(desserts);
}

這將輸出:[donuts, cookies, croissants, Junior Mints, Twizzlers, M&Ms]

條件式包含元素

可以使用 collection if 來條件式包含元素。例如:

void main() {
  bool peanutAllergy = true;
  List<String> sensitiveCandy = [
    'Junior Mints',
    'Twizzlers',
    if (!peanutAllergy) 'Reeses',
  ];

  print(sensitiveCandy);
}

這將輸出:[Junior Mints, Twizzlers]

集合迴圈

可以使用 collection for 來生成元素。例如:

void main() {
  List<String> deserts = ['gobi', 'sahara', 'arctic'];
  List<String> bigDeserts = [
    'ARABIAN',
    for (var desert in deserts) desert.toUpperCase(),
  ];

  print(bigDeserts);
}

這將輸出:[ARABIAN, GOBI, SAHARA, ARCTIC]

圖表翻譯:

  graph LR
    A[列表操作] --> B[新增元素]
    A --> C[合併列表]
    A --> D[條件式包含元素]
    A --> E[集合迴圈]
    B --> F[使用addAll方法]
    C --> G[使用spread operator]
    D --> H[使用collection if]
    E --> I[使用collection for]

這個圖表展示了 Dart 中的列表操作,包括新增元素、合併列表、條件式包含元素和集合迴圈。

處理可空列表

在 Dart 中,列表可以是可空的,也可以包含可空的元素。瞭解如何處理這些情況是非常重要的。

可空列表

首先,讓我們看看可空列表的例子:

List<int>? nullableList = [2, 4, 3, 7];
nullableList = null;

在這個例子中,nullableList 是一個可空的列表,意味著它可以是 null。注意,在列表型別後面加上了 ?,這表示列表本身是可空的。

可空元素

接下來,讓我們看看列表中包含可空元素的例子:

List<int?> nullableElements = [2, 4, null, 3, 7];

在這個例子中,nullableElements 是一個列表,包含可空的整數元素。注意,在列表型別後面加上了 ?,這表示列表中的元素是可空的。

可空列表與可空元素

最後,讓我們看看可空列表與可空元素的組合:

List<int?>? nullableListAndElements = [2, 4, null, 3, 7];
nullableListAndElements = null;

在這個例子中,nullableListAndElements 是一個可空的列表,包含可空的整數元素。

使用基本的空值感知運運算元

在處理可空列表或可空元素時,可以使用基本的空值感知運運算元。例如:

List<String?>? drinks = ['milk', 'water', null, 'soda'];

for (String? drink in drinks) {
  int letters = drink?.length ?? 0;
  print(letters);
}

在這個例子中,使用 ?. 空值感知運運算元來存取 drinklength 屬性,如果 drinknull,則傳回 0

內容解密:

在上面的例子中,使用 for 迴圈來迭代 drinks 列表。雖然列表是可空的,但 Dart 使用流分析來確定列表已經被指定,因此可以直接迭代元素。使用 ?. 空值感知運運算元來存取 drinklength 屬性,如果 drinknull,則傳回 0

圖表翻譯:

  flowchart TD
    A[開始] --> B[迭代列表]
    B --> C[存取元素的 length 屬性]
    C --> D[使用 ?. 空值感知運運算元]
    D --> E[傳回 0 如果元素是 null]
    E --> F[印出結果]

在這個圖表中,展示了迭代列表、存取元素的 length 屬性、使用 ?. 空值感知運運算元、傳回 0 如果元素是 null,以及印出結果的流程。

Dart 中的列表

Dart 的列表是一種有序的集合,允許您儲存和操作多個元素。列表的元素可以是任何型別的物件,包括字串、整數和其他列表。

列表的建立

您可以使用 [] 符號建立一個列表,例如:

List<String> fruits = ['apple', 'banana', 'orange'];

列表的索引

列表的索引從 0 開始,您可以使用 [] 符號訪問列表的元素,例如:

print(fruits[0]); // 輸出:apple

列表的可變性

列表的元素是可變的,您可以使用 [] 符號修改列表的元素,例如:

fruits[0] = 'grape';
print(fruits); // 輸出:[grape, banana, orange]

列表的迭代

您可以使用 for-in 迴圈迭代列表的元素,例如:

for (var fruit in fruits) {
  print(fruit);
}

列表的展開

您可以使用 ... 符號展開一個列表,例如:

List<String> moreFruits = ['watermelon', 'mango'];
fruits = [...fruits, ...moreFruits];
print(fruits); // 輸出:[grape, banana, orange, watermelon, mango]

列表的條件建立

您可以使用 collection ifcollection for 建立列表的內容,例如:

List<int> numbers = [1, 2, 3, 4, 5];
List<int> evenNumbers = [for (var number in numbers) if (number % 2 == 0) number];
print(evenNumbers); // 輸出:[2, 4]

列表的空安全操作

Dart 提供了兩個空安全運算子:?[]...?。這些運算子允許您安全地訪問和操作可能為空的列表。

例如:

List<String>? nullableList;
String? element = nullableList?[0];
print(element); // 輸出:null

挑戰

  1. 找到列表中最長和最短的字串。
  2. 判斷列表中是否包含重複的元素。
  3. 實作一個排序演算法來排序整數列表,不使用 sort 方法。

關鍵點

  • 列表儲存有序的集合。
  • 列表的元素可以使用零基索引訪問。
  • 列表的元素是可變的。
  • for-in 迴圈是迭代列表元素的方便方式。
  • 展開運算子 (...) 允許您展開一個列表。
  • 集合條件和迴圈可以用於建立列表的內容。
  • 空安全運算子 (?[]...?) 提供了額外的方式來處理可能為空的列表。

什麼是集合?

在數學中,集合是一組元素的集合,其中元素的順序不重要,且不允許重複的元素。在Dart中,集合的定義與數學中的定義非常相似。集合是一組元素的集合,其中元素的順序不重要,且不允許重複的元素。

集合的特性

集合的特性與列表(list)不同,列表中的元素的順序是重要的,且允許重複的元素。由於集合的特性,集合可以比列表更快地執行某些操作,特別是在處理大資料集時。這使得集合非常適合快速檢查元素是否存在於集合中。

建立集合

你可以使用Set型別注釋建立一個空集合,如下所示:

final Set<int> someSet = {};

這個語法告訴Dart,這個集合只允許整數。你也可以使用更短的語法:

final someSet = <int>{};

你也可以使用集合字面量建立集合:

final anotherSet = {1, 2, 3, 1};

由於集合字面量只包含整數,Dart可以推斷出集合的型別為Set<int>

集合操作

以下是一些集合操作的例子:

檢查集合內容

你可以使用contains方法檢查集合是否包含某個元素:

final desserts = {'cake', 'pie', 'donut'};
print(desserts.contains('cake')); // true
print(desserts.contains('cookies')); // false

contains方法傳回一個布林值,指示集合是否包含指定的元素。

新增元素

你可以使用add方法新增元素到集合中:

final drinks = <String>{};
drinks.add('cola');

刪除元素

你可以使用remove方法刪除元素從集合中:

final drinks = <String>{'cola', 'tea'};
drinks.remove('cola');

合併集合

你可以使用union方法合併兩個集合:

final set1 = {1, 2, 3};
final set2 = {3, 4, 5};
final unionSet = set1.union(set2);
print(unionSet); // {1, 2, 3, 4, 5}

交集

你可以使用intersection方法計算兩個集合的交集:

final set1 = {1, 2, 3};
final set2 = {3, 4, 5};
final intersectionSet = set1.intersection(set2);
print(intersectionSet); // {3}

差集

你可以使用difference方法計算兩個集合的差集:

final set1 = {1, 2, 3};
final set2 = {3, 4, 5};
final differenceSet = set1.difference(set2);
print(differenceSet); // {1, 2}

這些是集合的一些基本操作,你可以使用這些方法來操縱集合。

內容解密:

在這個例子中,我們使用了Set型別注釋建立了一個空集合,然後使用add方法新增元素到集合中。接著,我們使用contains方法檢查集合是否包含某個元素。最後,我們使用unionintersectiondifference方法合併、交集和差集兩個集合。

圖表翻譯:

  graph LR
    A[集合] --> B[新增元素]
    B --> C[刪除元素]
    C --> D[合併集合]
    D --> E[交集]
    E --> F[差集]

這個圖表展示了集合的一些基本操作,包括新增元素、刪除元素、合併集合、交集和差集。

集合的基本操作

集合(Set)是一種不允許重複元素的資料結構。在 Dart 中,集合的基本操作包括新增、移除和檢查元素是否存在。

從技術架構視角來看,Dart 的列表(List)和集合(Set)提供了豐富的功能來管理資料集合。深入剖析 List 的底層實作,可以發現其元素的插入和刪除操作在列表頭部或中部時,需要搬移元素,因此效率較低;相對地,若應用場景涉及頻繁的插入刪除,Linked List 則更具效能優勢。然而,Linked List 的記憶體開銷較大,需要權衡利弊。至於 Set,其不允許重複元素的特性使其在需要快速查詢元素是否存在,或進行集合運算(例如聯集、交集、差集)的場景中表現出色。技術選型時,需根據具體應用場景的效能需求和資料特性做出最佳選擇。對於注重執行效率的應用,例如需要頻繁插入刪除元素的場景,Linked List 或 Set 是更優的選擇;而對於資料量較小,且讀取操作較多的場景,List 則更為簡潔高效。未來,隨著 Dart 語言的發展,預計會出現更多針對不同資料結構的最佳化,進一步提升開發效率。玄貓認為,開發者應深入理解不同資料結構的特性和適用場景,才能寫出高效且穩定的 Dart 程式碼。