0%

[Flutter讀書會]Dart101:Build-inTypes資料型別-Booleans,Lists,Sets,Maps

前情提要

上一篇介紹完Strings之後,這篇來到BooleansListsSetsMaps了,因為Booleans就比較沒甚麼好解說的,就是truefalse,因此把另外三個也納進來一起解說,以免篇幅太少,基本上ListsSetsMaps也滿好理解的,這些資料型別是其他程式語言常見到的,只要搞清楚三者的差異以及適用時機,基本上就是駕輕就熟了,跟著筆者一起學習吧。

內容

筆者這邊還是簡單列一下筆者這邊的開發環境

  • 作業系統:Mac OS
  • Dart版本:2.17.6
  • 編輯器:Visual Studio Code

Booleans

這就沒甚麼好解釋的了,truefalse二擇一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Check for an empty string.
var fullName = '';
assert(fullName.isEmpty);

// Check for zero.
var hitPoints = 0;
assert(hitPoints <= 0);

// Check for null.
var unicorn;
assert(unicorn == null);

// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);

Lists

泛型集合List<T>,亦即可以示List<int>,也可以是List<string>,但不能將不同資料型別混著放,Dart程式語言是Type Safety的語言,因此編譯時就會噴錯。不意外地,Dart語言中,List使用zero-based indexing,這個東西平常看似沒甚麼,但是遇到時心裡就會很OOXX,筆者絕對不是在說JS中的getMonth(),因為常常遇到月份比較時,很自然地忘記它也是zero-based indexing,因此常常鬼打牆的在偵錯。

宣告方式

1
2
3
4
5
6
var list = [1, 2, 3];
var list = [
'Car',
'Boat',
'Plane',
];

Spread Operator

滿特別的一個,筆者在撰寫Angular時特別愛用,畢竟JS世界裡一切都是物件,因此若想要達到Deep Copy深層複製時,筆者愛用簡單解法,透過Spread Operator把每個元素都展開,並指派給新的變數,藉由達到Deep Copy深層複製的效果,竟然在Dart程式語言中再次遇見。

1
2
3
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);

Collection If

很特別的一個feature,在初始化塞值時,可以透過if判斷式,決定要不要塞該元素

1
2
// 依照promoActive的值為true或false來決定是否增加'Outlet'這個元素
var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];

Collection For

這也是跟上述feature一樣,比較少在其他語言看到的特性,可以透過for迴圈,塞有規則性的多個元素

1
2
3
4
var listOfInts = [1, 2, 3];
// 配合動態組合字串${expression},組出#1, #2, #3,並將這三個元素加入至listOfStrings陣列中
var listOfStrings = ['#0', for (var i in listOfInts) '#$i'];
assert(listOfStrings[1] == '#1');

相關操作API

  • isEmpty:判斷是否為空集合
  • add:新增一筆元素
  • addAll:新增多個元素
  • length:取得集合大小
  • removeAt:透過指定元素位置刪除(搭配indexOf找到元素所在位置)
  • clear:清空集合
  • filled:透過List提供的Constructor新增指定元素並且依照指定次數增加
  • sort:排序,可搭配使用compareTo使用自己指定的比較方法排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Create an empty list of strings.
var grains = <String>[];
assert(grains.isEmpty);

// Create a list using a list literal.
var fruits = ['apples', 'oranges'];

// Add to a list.
// 增加完成後,fruits為['apples', 'oranges', 'kiwis']
fruits.add('kiwis');

// Add multiple items to a list.
// 增加完成後,fruits為['apples', 'oranges', 'kiwis', 'grapes', 'bananas']
fruits.addAll(['grapes', 'bananas']);

// Get the list length.
// 執行到這邊,fruits這個List長度為5
assert(fruits.length == 5);

// Remove a single item.
// 先找到'apples'這個元素所在位置
var appleIndex = fruits.indexOf('apples');
// 透過指定元素位置方式刪除元素
fruits.removeAt(appleIndex);
// 執行到這邊,fruits這個List長度為4
assert(fruits.length == 4);

// Remove all elements from a list.
// 將fruits這個List清空
fruits.clear();
// 判斷是否為空集合,結果是true
assert(fruits.isEmpty);

// You can also create a List using one of the constructors.
// 第一個參數為增加次數
// 第二個參數為欲新增之元素
// 作用為拿第二個參數新增至List,次數為第一個參數指定之
// 因此vegetables這個List裡頭有99個元素,每個元素皆為'broccoli'
var vegetables = List.filled(99, 'broccoli');
assert(vegetables.every((v) => v == 'broccoli'));
1
2
3
4
5
6
7
8
9
10
11
var fruits = ['bananas', 'apples', 'oranges'];
print(fruits);
fruits.sort();
print(fruits);
// [bananas, apples, oranges]
// [apples, bananas, oranges]

// 透過compareTo來實作逆排
fruits.sort((a, b) => -a.compareTo(b));
print(fruits);
// [oranges, bananas, apples]

完整API資訊,請參考

https://api.dart.dev/stable/2.17.6/dart-core/List-class.html

Sets

泛型集合,與List不一樣的地方在於,Set每個元素都是唯一的,不能重複出現

A collection of objects in which each object can occur only once.

宣告方式

還記得List的宣告方式吧,[1, 2, 3]的方式,Set則是{ 1, 2, 3}

1
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

空集合的宣告方式則

1
2
3
4
5
6
// 讓Dart編譯器知道是Set<string>
var names = <String>{};
// 指定型別的宣告方式也是可以的
// Set<String> names = {}; // This works, too.
// 這是一個反教材,若沒有指定型別的{}則會被認定是Map的初始化,而非Set
// var names = {}; // Creates a map, not a set.

相關操作API,就不特別列出,因為大同小異,要特別提醒的是

A set in Dart is an unordered collection of unique items. Because a set is unordered, you can’t get a set’s items by index (position).

其他詳細API參考

https://api.dart.dev/stable/2.17.6/dart-core/Set-class.html

Maps

Maps結構就是Map<Tkey, TValue>(key, value)型式的一種資料型別,簡單點解釋的話,即是Dictionary字典型式的資料型別

宣告方式

1
2
3
4
5
6
7
8
9
10
11
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};

var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

Map部份,相關操作與Collection其他家族SetMap大同小異,而筆者想要經驗分享的是,Dictionary顧名思義,key是獨一無二的,因此每次在add時,記得使用containsKey這個API做檢查,讓你少走一點冤望路。

詳細API參考

https://api.dart.dev/stable/2.17.6/dart-core/Map-class.html

結論

趕緊在結束之前說一下List介紹中提到的

  • Spread Operator
  • Collection If
  • Collection For

因為ListSetMap皆屬於Collection大家族,因此皆適用。

ListSetMap要如何使用呢,一般情況使用List就可以解決大部份的問題,若想要集合的元素不重複時,可以考慮使用Set這個資料結構來存放,但注意Set這個沒有indexOf這個API可以使用,接著那種key、value式的就可以考慮使用Map來操作。

基本上記得每個資料結構的特性,搭配各自的API文件說明,筆者想應付一般應用程式是綽綽有餘的,筆者還是鼓勵一貫的做法,先理解一下能做到甚麼就好,理解的程度到在遇到問題時腦中有一些keyword浮現即可,然而真正要用到時再去深究它,畢竟腦容量有限,希望這篇文章對大家有幫助。

參考