詳解Flutter中Dart集合使用教程
前言
集合是應(yīng)用程序中最為常見(jiàn)的數(shù)據(jù)結(jié)構(gòu),Dart 一共支持如下四種集合,其中核心的 List, Map 和 Set 在基礎(chǔ)框架中,而 Queue 在 dart:collection 庫(kù)定義。
- 列表:也就是 List類(lèi),可動(dòng)態(tài)增長(zhǎng)的數(shù)組;
- key-value 集:即 Map<K, V> 類(lèi),用于存儲(chǔ)鍵值對(duì);
- 隊(duì)列:即 Queue類(lèi);
- 集合:即Set類(lèi),集合中的元素不可重復(fù)。
本篇介紹集合的最佳實(shí)踐。
優(yōu)先使用集合的特有語(yǔ)法
對(duì)于核心的集合類(lèi)List, Map 和 Set ,由于經(jīng)常使用,Dart 為這些類(lèi)提供的內(nèi)置的語(yǔ)法來(lái)快速構(gòu)建這些集合對(duì)象。
//?推薦用法
var?points?=?<Point>[];
var?addresses?=?<String,?Address>{};
var?counts?=?<int>{};
//?不推薦
var?addresses?=?Map<String,?Address>();
var?counts?=?Set<int>();
集合還有一些特殊的用法,比如使用展開(kāi)操作符(而且同時(shí)支持 ? 操作符判斷是否為空)將一個(gè)集合加入到另一個(gè)集合。同時(shí)還支持結(jié)合 if 和 for 來(lái)控制元素的加入。
//?推薦用法
var?arguments?=?[
??...options,
??command,
??...?modeFlags,
??for?(var?path?in?filePaths)
????if?(path.endsWith('.dart'))
??????path.replaceAll('.dart',?'.js')
];
//?不推薦
var?arguments?=?<String>[];
arguments.addAll(options);
arguments.add(command);
if?(modeFlags?!=?null)?arguments.addAll(modeFlags);
arguments.addAll(filePaths
????.where((path)?=>?path.endsWith('.dart'))
????.map((path)?=>?path.replaceAll('.dart',?'.js')));
上面的推薦用法其實(shí)除了展開(kāi)操作符以外,使用 if 和 for 的并不常見(jiàn)。說(shuō)實(shí)話,個(gè)人挺不習(xí)慣這種寫(xiě)法的,感覺(jué)可讀性并不高。
不要使用.length 屬性判斷集合是不是為空
由于集合遵循的是 Iterable 協(xié)議,這個(gè)協(xié)議并不需要集合隨時(shí)知道它的長(zhǎng)度。因此調(diào)用.length 的時(shí)候,其實(shí)相當(dāng)于是遍歷了一遍,執(zhí)行速度是很低的。這是獲取 length 的實(shí)現(xiàn)方法:
int?get?length?{
??assert(this?is!?EfficientLengthIterable);
??int?count?=?0;
??Iterator?it?=?iterator;
??while?(it.moveNext())?{
????count++;
??}
??return?count;
}
因此,更高效地判斷集合是否為空的做法是使用.isEmpty 或 .isNotEmpty。
bool?get?isEmpty?=>?!iterator.moveNext();
因此,不要用.length == 0來(lái)判斷集合是否為空。
//?正確示例
if?(lunchBox.isEmpty)?return?'so?hungry...';
if?(words.isNotEmpty)?return?words.join('?');
//?錯(cuò)誤示例
if?(lunchBox.length?==?0)?return?'so?hungry...';
if?(!words.isEmpty)?return?words.join('?');
避免使用 forEach 迭代元素
在 JS 中,會(huì)使用 forEacth 方法來(lái)迭代元素,這是因?yàn)閮?nèi)置的 for-in 循環(huán)和我們想要的不一樣。但是在 Dart 中的 for-in 循環(huán)是正常的迭代,這樣會(huì)簡(jiǎn)化我們的代碼。
//?正確示例
for?(final?person?in?people)?{
??...
}
//?錯(cuò)誤示例
people.forEach((person)?{
??...
});
但是如果我們是要對(duì)每個(gè)元素進(jìn)行操作的話,那么可以直接將這個(gè)操作作為方法傳遞到 forEacth 中,這樣的代碼更簡(jiǎn)潔。
people.forEach(print);
注意 Map 是不可迭代的,因此使用 forEach 是沒(méi)問(wèn)題的。
不要使用 List.from(),除非你想要更改結(jié)果的類(lèi)型
下面是兩行對(duì)比的代碼:
var?list?=?['a',?'b']; var?copy1?=?list.toList(); var?copy2?=?List.from(list); print(copy1.runtimeType); print(copy2.runtimeType);
猜猜打印出來(lái)的結(jié)果會(huì)是什么?
List<String>
List<dynamic>
如果使用 List.from 方法的話,如果不指定泛型類(lèi)型,會(huì)抹除集合的類(lèi)型,變成 dynamic?。?!因此,除非某些對(duì)象需要做這樣的類(lèi)型轉(zhuǎn)換,否則不應(yīng)該使用 List.from 方法。當(dāng)然,List.from 也不是沒(méi)有用,比如數(shù)值類(lèi)型支持強(qiáng)制轉(zhuǎn)換,可以指定類(lèi)型做強(qiáng)制轉(zhuǎn)換,例如下面剩下的因?yàn)槎际钦麛?shù)了,因此可以轉(zhuǎn)為 List類(lèi)型``。
var?numbers?=?[1,?2.3,?4];?//?List<num>. numbers.removeAt(1);?//?Now?it?only?contains?integers. var?ints?=?List<int>.from(numbers);
使用 whereType 過(guò)濾類(lèi)型
如果要從動(dòng)態(tài)集合篩選某個(gè)類(lèi)型的子集,那么應(yīng)該使用 whereType<T>方法,而不是使用 where 來(lái)過(guò)濾。
var?list?=?['1',?'2',?1,?2]; //?正確示例 var?intList?=?list.whereType<int>(); //?錯(cuò)誤示例 var?intList?=?list.where((e)?=>?e?is?int);
這是因?yàn)椋?code>where 方法返回的仍然是一個(gè) WhereIterable<Object>對(duì)象,而不是我們想要的WhereIterable<int> 對(duì)象,這意味如果使用 where 還需要做一次強(qiáng)制轉(zhuǎn)換,這并不推薦。
//?錯(cuò)誤示例 var?list?=?['1',?'2',?1,?2]; var?intList?=?list.where((e)?=>?e?is?int).cast<int>();
如果有別的方式的話,不要使用 cast 做強(qiáng)制轉(zhuǎn)換
通常,當(dāng)在處理迭代對(duì)象或 stream 的時(shí)候,我們會(huì)對(duì)其做一系列的操作。之后,我們會(huì)指定一個(gè)類(lèi)型的對(duì)象。相對(duì)于使用 cast() 方法,我們應(yīng)該使用其他可能存在的轉(zhuǎn)換方式。例如,當(dāng)我們使用 toList 的時(shí)候,可以使用 List<T>.from 來(lái)進(jìn)行類(lèi)型轉(zhuǎn)換。
//?正確示例 var?stuff?=?<dynamic>[1,?2]; var?ints?=?List<int>.from(stuff); //?錯(cuò)誤示例 var?stuff?=?<dynamic>[1,?2]; var?ints?=?stuff.toList().cast<int>();
我們也可以使用 map<T> 來(lái)將集合轉(zhuǎn)為另一個(gè)類(lèi)型的集合。
//?正確示例 var?stuff?=?<dynamic>[1,?2]; var?reciprocals?=?stuff.map<double>((n)?=>?1?/?n); //?錯(cuò)誤示例 var?stuff?=?<dynamic>[1,?2]; var?reciprocals?=?stuff.map((n)?=>?1?/?n).cast<double>();
避免使用 cast() 做強(qiáng)制轉(zhuǎn)換
當(dāng)我們沒(méi)有其他辦法進(jìn)行類(lèi)型轉(zhuǎn)換時(shí),那么也需要盡可能地避免使用 cast() 做類(lèi)型轉(zhuǎn)換。這里有幾條建議能夠避免使用強(qiáng)制轉(zhuǎn)換:
正確地定義集合類(lèi)型,如果集合類(lèi)型是明確的,那么就應(yīng)該在集合對(duì)象定義時(shí)明確類(lèi)型。例如下面的例子:
//?正確示例
List<int>?singletonList(int?value)?{
??var?list?=?<int>[];
??list.add(value);
??return?list;
}
//?錯(cuò)誤示例
List<int>?singletonList(int?value)?{
??var?list?=?[];?//?List<dynamic>.
??list.add(value);
??return?list.cast<int>();
}
在訪問(wèn)元素時(shí)進(jìn)行轉(zhuǎn)換,當(dāng)進(jìn)行集合迭代的時(shí)候,可以在迭代過(guò)程中對(duì)每個(gè)元素進(jìn)行類(lèi)型轉(zhuǎn)換。
//?正確示例
void?printEvens(List<Object>?objects)?{
??//?假設(shè)我們知道集合只有整數(shù)
??for?(final?n?in?objects)?{
????if?((n?as?int).isEven)?print(n);
??}
}
//?錯(cuò)誤示例
void?printEvens(List<Object>?objects)?{
??//?假設(shè)我們知道集合只有整數(shù)
??for?(final?n?in?objects.cast<int>())?{
????if?(n.isEven)?print(n);
??}
}
優(yōu)先使用 List.from() 做轉(zhuǎn)換。如果集合的大部分元素都會(huì)被訪問(wèn)到,而且不再需要對(duì)轉(zhuǎn)換前的做處理,那么就使用 List.from 來(lái)做轉(zhuǎn)換。cast()方法返回的是一個(gè)延遲處理的集合,當(dāng)需要使用元素時(shí)才會(huì)執(zhí)行轉(zhuǎn)換。對(duì)于轉(zhuǎn)換少量元素而言,這樣效率會(huì)高。但是,大部分情況下,將對(duì)象包裝為延遲對(duì)象的缺陷更明顯。
//?正確示例
int?median(List<Object>?objects)?{
??//?假設(shè)我們知道集合只有整數(shù)
??var?ints?=?List<int>.from(objects);
??ints.sort();
??return?ints[ints.length?~/?2];
}
//??錯(cuò)誤示例
int?median(List<Object>?objects)?{
??//?假設(shè)我們知道集合只有整數(shù)
??var?ints?=?objects.cast<int>();
??ints.sort();
??return?ints[ints.length?~/?2];
}
總結(jié)
本篇總結(jié)了 Dart 語(yǔ)言中使用集合的一些場(chǎng)景的最佳實(shí)踐,實(shí)際上很多要點(diǎn)我們?cè)谄綍r(shí)并不會(huì)注意 —— 抱著能用就行了的態(tài)度。但是,這些內(nèi)容官方早就有了指引,知道何為正確會(huì)有助于我們編寫(xiě)質(zhì)量更高的代碼!
以上就是詳解Flutter中Dart集合使用教程的詳細(xì)內(nèi)容,更多關(guān)于Flutter Dart集合的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android中使用listview實(shí)現(xiàn)qq/微信好友列表
本文主要介紹了android中使用listview實(shí)現(xiàn)qq/微信好友列表(頭像,昵稱(chēng),個(gè)性簽名)的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
Android的Launcher啟動(dòng)器中添加快捷方式及小部件實(shí)例
這篇文章主要介紹了在Android的Launcher啟動(dòng)器中添加快捷方式及窗口小部件的方法,包括在自己的應(yīng)用程序中添加窗口小部件AppWidget的例子,需要的朋友可以參考下2016-02-02
Android實(shí)現(xiàn)下載m3u8視頻文件問(wèn)題解決
這篇文章主要介紹了Android實(shí)現(xiàn)下載m3u8視頻文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-01-01
Android系統(tǒng)實(shí)現(xiàn)DroidPlugin插件機(jī)制
這篇文章主要為大家詳細(xì)介紹了Android系統(tǒng)上實(shí)現(xiàn)DroidPlugin插件機(jī)制,可以在無(wú)需安裝、修改的情況下運(yùn)行APK文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
Android開(kāi)發(fā)實(shí)現(xiàn)的幾何圖形工具類(lèi)GeometryUtil完整實(shí)例
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)的幾何圖形工具類(lèi)GeometryUtil,涉及Android坐標(biāo)圖形數(shù)值運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
Android網(wǎng)絡(luò)請(qǐng)求-sign參數(shù)的設(shè)置方式
這篇文章主要介紹了Android網(wǎng)絡(luò)請(qǐng)求-sign參數(shù)的設(shè)置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android編程之Button控件配合Toast控件用法分析
這篇文章主要介紹了Android編程之Button控件配合Toast控件用法,結(jié)合實(shí)例形式分析了Button控件及Toast控件的功能及具體使用技巧,需要的朋友可以參考下2015-12-12

