dart系列之:集合使用最佳實踐

flydean發表於2022-02-23

簡介

dart中有四種集合,分別是Set,List,Map和queues。這些集合在使用中需要注意些什麼呢?什麼樣的使用才是最好的使用方法呢?一起來看看吧。

使用字面量建立集合

對於常用的Set,Map和List三個集合來說,他們是有自己的無參建構函式的:

  factory Set() = LinkedHashSet<E>;
  external factory Map();

  @Deprecated("Use a list literal, [], or the List.filled constructor instead")
  external factory List([int? length]);

可以看到Set和Map是可以使用建構函式的。但是對於List來說,無參的建構函式已經不推薦使用了。

對於Set和Map來說,可以這樣構造:

var studentMap = Map<String, Student>();
var ages = Set<int>();

但是dart官方推薦直接使用字面量來建立這些集合,如下所示:

var studentMap = <String, Student>{};
var ages = <int>{};

為什麼呢?這是因為dart中的字面量集合是非常強大的。可以通過擴充套件運算子,if和for語句對集合進行構造和擴充套件,如下所示:

var studentList = [
  ...list1,
  student1,
  ...?list2,
  for (var name in list3)
    if (name.endsWith('jack'))
      name.replaceAll('jack', 'mark')
];

不要使用.length來判斷集合是否為空

對應dart的可遍歷集合來說,這些集合並沒有儲存集合的長度資訊,所以如果你呼叫集合的.length方法,可能會導致集合的遍歷,從而影響效能。

注意Set和List是可遍歷的,而Map是不可遍歷的。

所以,我們需要呼叫集合的.isEmpty 和 .isNotEmpty方法來判斷集合是否為空,這樣速度更快。

if (studentList.isEmpty) print('it is empty');
if (studentList.isNotEmpty) print('it is not empty');

可遍歷物件的遍歷

對應Set和List這兩個可遍歷的集合來說,有兩種遍歷方法,可以通過呼叫forEach() 方法或者for-in來進行遍歷,如下所示:

for (final student in studentList) {
  ...
}
studentList.forEach((student) {
  ...
});

這兩種方法中,dart推薦使用for in的寫法。

當然,如果你想將現有的function應用在集合中的每個元素中,forEach也是可以的:

studentList.forEach(print);
注意,因為Map是不可遍歷的,所以上面的規則對Map並不適用。

List.from和iterable.toList

可遍歷物件可以通過呼叫toList將其轉換成為List,同樣的List.from也可以將可遍歷物件轉換成為List。

那麼兩者有什麼區別呢?

var list1 = iterable.toList();
var list2 = List.from(iterable);

兩者的區別是iterable.toList並不會改變list中資料的型別,而List.from會. 舉個例子:

// Creates a List<String>:
var studentList = ['jack', 'mark', 'alen'];

// Prints "List<String>":
print(studentList.toList().runtimeType);

// Prints "List<dynamic>":
print(List.from(studentList).runtimeType);

當然,你也可以使用List<T>.from來強制對建立出來的List進行型別轉換。

List<String>.from(studentList)

where和whereType

對於可遍歷物件來說,兩個過濾集合中元素的方法,他們是where和whereType。

比如,我們需要過濾List中的字串,則可以這樣寫:

var studentList = ['jack', 'ma', 18, 31];
var students1 = studentList.where((e) => e is String);
var students2 = studentList.whereType<String>();

看上去兩者沒有太大的區別,都可以得到應有的結果。但是兩者事實上還是有區別的,因為對應where來說,返回的是一個Iterable<Object>,所以上面的例子中,如果我們真的需要返回String,還需要對返回結果進行case:

var students1 = studentList.where((e) => e is String).cast<String>();;

所以,如果你要返回特定的物件時候,記得使用whereType。

避免使用cast

cast通常用來對集合中的元素進行型別轉換操作,但是cast的效能比較低,所以在萬不得已的情況下,一定要避免使用cast。

那麼如果不使用cast,我們怎麼對型別進行轉換呢?

一個基本的原則就是在構建集合的時候提前進行型別轉換,而不是在構建集合之後再進行整體的cast。

比如下面的例子從一個dynamic型別的List轉換成為int型別的List,那麼我們可以在呼叫List.from方法的時候進行型別轉換:

var stuff = <dynamic>[1, 2];
var ints = List<int>.from(stuff);

如果是map的話,可以這樣操作:

var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map<double>((n) => 1 / n);

比如我們需要構建一個int的List,那麼在建立之初就可以指定List的內部型別,然後再對其新增元素:

List<int> singletonList(int value) {
  var list = <int>[];
  list.add(value);
  return list;
}

總結

以上就是dart中的集合使用最佳實踐。

本文已收錄於 http://www.flydean.com/30-dart-collection/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!

相關文章