網站首頁 編程語言 正文
前言
集合是應用程序中最為常見的數據結構,Dart 一共支持如下四種集合,其中核心的?List
,?Map
?和?Set
?在基礎框架中,而?Queue
?在?dart:collection
?庫定義。
- 列表:也就是 List類,可動態增長的數組;
- key-value 集:即 Map<K, V> 類,用于存儲鍵值對;
- 隊列:即 Queue類;
- 集合:即Set類,集合中的元素不可重復。
本篇介紹集合的最佳實踐。
優先使用集合的特有語法
對于核心的集合類List
,?Map
?和?Set
?,由于經常使用,Dart 為這些類提供的內置的語法來快速構建這些集合對象。
//?推薦用法 var?points?=?<Point>[]; var?addresses?=?<String,?Address>{}; var?counts?=?<int>{}; //?不推薦 var?addresses?=?Map<String,?Address>(); var?counts?=?Set<int>();
集合還有一些特殊的用法,比如使用展開操作符(而且同時支持 ? 操作符判斷是否為空)將一個集合加入到另一個集合。同時還支持結合 if 和 for 來控制元素的加入。
//?推薦用法 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')));
上面的推薦用法其實除了展開操作符以外,使用 if 和 for 的并不常見。說實話,個人挺不習慣這種寫法的,感覺可讀性并不高。
不要使用.length 屬性判斷集合是不是為空
由于集合遵循的是?Iterable
?協議,這個協議并不需要集合隨時知道它的長度。因此調用.length
?的時候,其實相當于是遍歷了一遍,執行速度是很低的。這是獲取?length
?的實現方法:
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來判斷集合是否為空。
//?正確示例 if?(lunchBox.isEmpty)?return?'so?hungry...'; if?(words.isNotEmpty)?return?words.join('?'); //?錯誤示例 if?(lunchBox.length?==?0)?return?'so?hungry...'; if?(!words.isEmpty)?return?words.join('?');
避免使用 forEach 迭代元素
在 JS 中,會使用 forEacth 方法來迭代元素,這是因為內置的 for-in 循環和我們想要的不一樣。但是在 Dart 中的 for-in 循環是正常的迭代,這樣會簡化我們的代碼。
//?正確示例 for?(final?person?in?people)?{ ??... } //?錯誤示例 people.forEach((person)?{ ??... });
但是如果我們是要對每個元素進行操作的話,那么可以直接將這個操作作為方法傳遞到?forEacth
?中,這樣的代碼更簡潔。
people.forEach(print);
注意?Map
?是不可迭代的,因此使用?forEach
?是沒問題的。
不要使用 List.from(),除非你想要更改結果的類型
下面是兩行對比的代碼:
var?list?=?['a',?'b']; var?copy1?=?list.toList(); var?copy2?=?List.from(list); print(copy1.runtimeType); print(copy2.runtimeType);
猜猜打印出來的結果會是什么?
List<String>
List<dynamic>
如果使用?List.from
?方法的話,如果不指定泛型類型,會抹除集合的類型,變成?dynamic
!!!因此,除非某些對象需要做這樣的類型轉換,否則不應該使用?List.from
?方法。當然,List.from
?也不是沒有用,比如數值類型支持強制轉換,可以指定類型做強制轉換,例如下面剩下的因為都是整數了,因此可以轉為 List類型``。
var?numbers?=?[1,?2.3,?4];?//?List<num>. numbers.removeAt(1);?//?Now?it?only?contains?integers. var?ints?=?List<int>.from(numbers);
使用 whereType 過濾類型
如果要從動態集合篩選某個類型的子集,那么應該使用?whereType<T>
方法,而不是使用?where
?來過濾。
var?list?=?['1',?'2',?1,?2]; //?正確示例 var?intList?=?list.whereType<int>(); //?錯誤示例 var?intList?=?list.where((e)?=>?e?is?int);
這是因為,where
?方法返回的仍然是一個?WhereIterable<Object>
對象,而不是我們想要的WhereIterable<int>
?對象,這意味如果使用?where
?還需要做一次強制轉換,這并不推薦。
//?錯誤示例 var?list?=?['1',?'2',?1,?2]; var?intList?=?list.where((e)?=>?e?is?int).cast<int>();
如果有別的方式的話,不要使用 cast 做強制轉換
通常,當在處理迭代對象或?stream
?的時候,我們會對其做一系列的操作。之后,我們會指定一個類型的對象。相對于使用?cast()
?方法,我們應該使用其他可能存在的轉換方式。例如,當我們使用 toList 的時候,可以使用?List<T>.from
?來進行類型轉換。
//?正確示例 var?stuff?=?<dynamic>[1,?2]; var?ints?=?List<int>.from(stuff); //?錯誤示例 var?stuff?=?<dynamic>[1,?2]; var?ints?=?stuff.toList().cast<int>();
我們也可以使用?map<T>
?來將集合轉為另一個類型的集合。
//?正確示例 var?stuff?=?<dynamic>[1,?2]; var?reciprocals?=?stuff.map<double>((n)?=>?1?/?n); //?錯誤示例 var?stuff?=?<dynamic>[1,?2]; var?reciprocals?=?stuff.map((n)?=>?1?/?n).cast<double>();
避免使用 cast() 做強制轉換
當我們沒有其他辦法進行類型轉換時,那么也需要盡可能地避免使用?cast()?
做類型轉換。這里有幾條建議能夠避免使用強制轉換:
正確地定義集合類型,如果集合類型是明確的,那么就應該在集合對象定義時明確類型。例如下面的例子:
//?正確示例 List<int>?singletonList(int?value)?{ ??var?list?=?<int>[]; ??list.add(value); ??return?list; } //?錯誤示例 List<int>?singletonList(int?value)?{ ??var?list?=?[];?//?List<dynamic>. ??list.add(value); ??return?list.cast<int>(); }
在訪問元素時進行轉換,當進行集合迭代的時候,可以在迭代過程中對每個元素進行類型轉換。
//?正確示例 void?printEvens(List<Object>?objects)?{ ??//?假設我們知道集合只有整數 ??for?(final?n?in?objects)?{ ????if?((n?as?int).isEven)?print(n); ??} } //?錯誤示例 void?printEvens(List<Object>?objects)?{ ??//?假設我們知道集合只有整數 ??for?(final?n?in?objects.cast<int>())?{ ????if?(n.isEven)?print(n); ??} }
優先使用?List.from()
?做轉換。如果集合的大部分元素都會被訪問到,而且不再需要對轉換前的做處理,那么就使用?List.from
?來做轉換。cast()方法返回的是一個延遲處理的集合,當需要使用元素時才會執行轉換。對于轉換少量元素而言,這樣效率會高。但是,大部分情況下,將對象包裝為延遲對象的缺陷更明顯。
//?正確示例 int?median(List<Object>?objects)?{ ??//?假設我們知道集合只有整數 ??var?ints?=?List<int>.from(objects); ??ints.sort(); ??return?ints[ints.length?~/?2]; } //??錯誤示例 int?median(List<Object>?objects)?{ ??//?假設我們知道集合只有整數 ??var?ints?=?objects.cast<int>(); ??ints.sort(); ??return?ints[ints.length?~/?2]; }
總結
本篇總結了 Dart 語言中使用集合的一些場景的最佳實踐,實際上很多要點我們在平時并不會注意 —— 抱著能用就行了的態度。但是,這些內容官方早就有了指引,知道何為正確會有助于我們編寫質量更高的代碼!
原文鏈接:https://mp.weixin.qq.com/s/n-hi3vbC7-VBLrb5lLEo3g
相關推薦
- 2022-05-23 iOS實現垂直滑動條效果_IOS
- 2022-05-02 Pyinstaller+Pipenv打包Python文件的實現示例_python
- 2024-03-18 sql篇-輸入數據提示[HY000][1366] Incorrect string value: ‘
- 2022-05-02 詳解在Python中使用OpenCV進行直線檢測_python
- 2022-06-18 Android自定義雙向滑動控件_Android
- 2022-04-28 Python中自定義函方法與參數具有默認值的函數_python
- 2022-09-22 linux進程概念
- 2022-05-07 Python真題案例之蛇形數組詳解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支