【Flutter】Dart語法篇之集合的使用與源碼解析(二)

一惋增、List

在dart中的List集合是具有長(zhǎng)度的可索引對(duì)象集合,它沒有委托dart:collection包中集合實(shí)現(xiàn)痘煤,完全由內(nèi)部自己實(shí)現(xiàn)拨与。

初始化

main() { //初始化一:直接使用[]形式初始化 List colorList1 = ['red', 'yellow', 'blue', 'green'];

//初始化二: var + 泛型
    var colorList2 = <String> ['red', 'yellow', 'blue', 'green'];

    //初始化三: 初始化定長(zhǎng)集合
    List<String> colorList3 = List(4);//初始化指定大小為4的集合,
    colorList3.add('deepOrange');//注意: 一旦指定了集合長(zhǎng)度,不能再調(diào)用add方法张肾,否則會(huì)拋出Cannot add to a fixed-length list芭析。也容易理解因?yàn)橐粋€(gè)定長(zhǎng)的集合不能再擴(kuò)展了。
   print(colorList3[2]);//null,此外初始化4個(gè)元素默認(rèn)都是null

   //初始化四: 初始化空集合且是可變長(zhǎng)的
   List<String> colorList4 = List();//相當(dāng)于List<String> colorList4 =  []
   colorList4[2] = 'white';//這里會(huì)報(bào)錯(cuò)吞瞪,[]=實(shí)際上就是一個(gè)運(yùn)算符重載馁启,表示修改指定index為2的元素為white,然而它長(zhǎng)度為0所以找不到index為2元素芍秆,所以會(huì)拋出IndexOutOfRangeException
}

遍歷

main() {
  List<String> colorList = ['red', 'yellow', 'blue', 'green']; //for-i遍歷
  for (var i = 0; i < colorList.length; i++) {
    //可以使用var或int
    print(colorList[i]);
  } //forEach遍歷
  colorList.forEach((color) => print(color)); //forEach的參數(shù)為Function. =>使用了箭頭函數(shù)
  //for-in遍歷
  for (var color in colorList) {
    print(color);
  }
  //while+iterator迭代器遍歷惯疙,類似Java中的iteator
  while (colorList.iterator.moveNext()) {
    print(colorList.iterator.current);
  }
}

常用的函數(shù)

main() {
  List<String> colorList = ['red', 'yellow', 'blue', 'green'];
  colorList.add('white');
//和Kotlin類似通過add添加一個(gè)新的元素
  List<String> newColorList = ['white', 'black'];
  colorList.addAll(newColorList); //addAll添加批量元素
  print(colorList[2]); //可以類似Kotlin一樣,直接使用數(shù)組下標(biāo)形式訪問元素
  print(colorList.length); //獲取集合的長(zhǎng)度妖啥,這個(gè)Kotlin不一樣霉颠,Kotlin中使用的是size
  colorList.insert(1, 'black'); //在集合指定index位置插入指定的元素
  colorList.removeAt(2); //移除集合指定的index=2的元素,第3個(gè)元素
  colorList.clear(); //清除所有元素
  print(colorList.sublist(1, 3)); //截取子集合
  print(colorList.getRange(1, 3)); //獲取集合中某個(gè)范圍元素
  print(colorList.join(
      '<--->')); //類似Kotlin中的joinToString方法荆虱,輸出: red<--->yellow<--->blue<--->green
  print(colorList.isEmpty);
  print(colorList.contains('green'));
}

構(gòu)造函數(shù)源碼分析
dart中的List有很多個(gè)構(gòu)造器蒿偎,一個(gè)主構(gòu)造器和多個(gè)命名構(gòu)造器。主構(gòu)造器中有個(gè)length可選參數(shù).

external factory List([int length]);//主構(gòu)造器怀读,傳入length可選參數(shù)诉位,默認(rèn)為0

external factory List.filled(int length, E fill, {bool growable = false});//filled命名構(gòu)造器,只能聲明定長(zhǎng)的數(shù)組

external factory List.from(Iterable elements, {bool growable = true});

factory List.of(Iterable elements, {bool growable = true}) => List.from(elements, growable: growable);//委托給List.from構(gòu)造器來實(shí)現(xiàn)

external factory List.unmodifiable(Iterable elements);

exteranl關(guān)鍵字(插播一條內(nèi)容)
注意: 問題來了菜枷,可能大家看到List源碼的時(shí)候一臉懵逼苍糠,構(gòu)造函數(shù)沒有具體的實(shí)現(xiàn)。不知道有沒有注意 到exteranl 關(guān)鍵字啤誊。external修飾的函數(shù)具有一種實(shí)現(xiàn)函數(shù)聲明和實(shí)現(xiàn)體分離的特性岳瞭。這下應(yīng)該就明白了,也就是對(duì)應(yīng)實(shí)現(xiàn)在別的地方蚊锹。實(shí)際上你可以在DartSDK中的源碼找到瞳筏,以List舉例,對(duì)應(yīng)的是 sdk/sdk_nnbd/lib/_internal/vm/lib/array_patch.dart, 此外對(duì)應(yīng)的external函數(shù)實(shí)現(xiàn)會(huì)有一個(gè) @patch注解 修飾.

@patch
class List<E> {
  //對(duì)應(yīng)的是List主構(gòu)造函數(shù)的實(shí)現(xiàn)
  @patch
  factory List([int length]) native "List_new";//實(shí)際上這里是通過native層的c++數(shù)組來實(shí)現(xiàn)牡昆,具體可參考runtime/lib/array.cc

 //對(duì)應(yīng)的是List.filled構(gòu)造函數(shù)的實(shí)現(xiàn),fill是需要填充元素值, 默認(rèn)growable是false姚炕,默認(rèn)不具有擴(kuò)展功能
  @patch
  factory List.filled(int length, E fill, {bool growable: false}) {
    var result = growable ? new _GrowableList<E>(length) : new _List<E>(length);//可以看到如果是可變長(zhǎng),就會(huì)創(chuàng)建一個(gè)_GrowableList迁杨,否則就創(chuàng)建內(nèi)部私有的_List
    if (fill != null) {//fill填充元素值不為null,就返回length長(zhǎng)度填充值為fill的集合
      for (int i = 0; i < length; i++) {
        result[i] = fill;
      }
    }
    return result;//否則直接返回相應(yīng)長(zhǎng)度的空集合
  }

 //對(duì)應(yīng)的是List.from構(gòu)造函數(shù)的實(shí)現(xiàn),可將Iterable的集合加入到一個(gè)新的集合中钻心,默認(rèn)growable是true,默認(rèn)具備擴(kuò)展功能
  @patch
  factory List.from(Iterable elements, {bool growable: true}) {
    if (elements is EfficientLengthIterable<E>) {
      int length = elements.length;
      var list = growable ? new _GrowableList<E>(length) : new _List<E>(length);//如果是可變長(zhǎng)凄硼,就會(huì)創(chuàng)建一個(gè)_GrowableList铅协,否則就創(chuàng)建內(nèi)部私有的_List
      if (length > 0) {
        //只有在必要情況下創(chuàng)建iterator
        int i = 0;
        for (var element in elements) {
          list[i++] = element;
        }
      }
      return list;
    }
    //如果elements是一個(gè)Iterable<E>,就不需要為每個(gè)元素做類型測(cè)試
    //因?yàn)樵谝话闱闆r下,如果elements是Iterable<E>摊沉,在開始循環(huán)之前會(huì)用單個(gè)類型測(cè)試替換其中每個(gè)元素的類型測(cè)試狐史。但是注意下: 等等,我發(fā)現(xiàn)下面這段源碼好像有點(diǎn)問題,難道是我眼神不好骏全,if和else內(nèi)部執(zhí)行代碼一樣苍柏。    
    if (elements is Iterable<E>) {
      //創(chuàng)建一個(gè)_GrowableList
      List<E> list = new _GrowableList<E>(0);
      //遍歷elements將每個(gè)元素重新加入到_GrowableList中
      for (E e in elements) {
        list.add(e);
      }
      //如果是可變長(zhǎng)的直接返回這個(gè)list即可
      if (growable) return list;
      //否則調(diào)用makeListFixedLength使得集合變?yōu)槎ㄩL(zhǎng)集合,實(shí)際上調(diào)用native層的c++實(shí)現(xiàn)
      return makeListFixedLength(list);
    } else {
      List<E> list = new _GrowableList<E>(0);
      for (E e in elements) {
        list.add(e);
      }
      if (growable) return list;
      return makeListFixedLength(list);
    }
  }

  //對(duì)應(yīng)的是List.unmodifiable構(gòu)造函數(shù)的實(shí)現(xiàn)
  @patch
  factory List.unmodifiable(Iterable elements) {
    final result = new List<E>.from(elements, growable: false);
    //這里利用了List.from構(gòu)造函數(shù)創(chuàng)建一個(gè)定長(zhǎng)的集合result
    return makeFixedListUnmodifiable(result);
  }
  ...
}

對(duì)應(yīng)的List.from sdk的源碼解析

//sdk/lib/_internal/vm/lib/internal_patch.dart中的makeListFixedLength
@patch
List<T> makeListFixedLength<T>(List<T> growableList)
 native "Internal_makeListFixedLength";

//runtime/lib/growable_array.cc 中的Internal_makeListFixedLength
DEFINE_NATIVE_ENTRY(Internal_makeListFixedLength, 0, 1) {
 GET_NON_NULL_NATIVE_ARGUMENT(GrowableObjectArray, array,
 arguments->NativeArgAt(0));
 return Array::MakeFixedLength(array, /* unique = */ true);//調(diào)用Array::MakeFixedLength C++方法變?yōu)槎ㄩL(zhǎng)集合
}

//runtime/vm/object.cc中的Array::MakeFixedLength 返回一個(gè)RawArray
RawArray* Array::MakeFixedLength(const GrowableObjectArray& growable_array, bool unique) {

 ASSERT(!growable_array.IsNull());
 Thread* thread = Thread::Current();
 Zone* zone = thread->zone();
 intptr_t used_len = growable_array.Length();
 //拿到泛型類型參數(shù),然后準(zhǔn)備復(fù)制它們
 const TypeArguments& type_arguments =
 TypeArguments::Handle(growable_array.GetTypeArguments());

 //如果集合為空
 if (used_len == 0) {
 //如果type_arguments是空姜贡,那么它就是一個(gè)原生List试吁,不帶泛型類型參數(shù)的
    if (type_arguments.IsNull() && !unique) {
     //這是一個(gè)原生List(沒有泛型類型參數(shù))集合并且是非unique,直接返回空數(shù)組
         return Object::empty_array().raw();
    }
 // 根據(jù)傳入List的泛型類型參數(shù)楼咳,創(chuàng)建一個(gè)新的空的數(shù)組
    Heap::Space space = thread->IsMutatorThread() ? Heap::kNew : Heap::kOld;//如果是MutatorThread就開辟新的內(nèi)存空間否則復(fù)用舊的
    Array& array = Array::Handle(zone, Array::New(0, space));//創(chuàng)建一個(gè)新的空數(shù)組array
    array.SetTypeArguments(type_arguments);//設(shè)置拿到的類型參數(shù)
    return array.raw();//返回一個(gè)相同泛型參數(shù)的新數(shù)組
 }

 //如果集合不為空熄捍,取出growable_array中的data數(shù)組,且返回一個(gè)帶數(shù)據(jù)新的數(shù)組array
 const Array& array = Array::Handle(zone, growable_array.data());
    ASSERT(array.IsArray());
    array.SetTypeArguments(type_arguments);//設(shè)置拿到的類型參數(shù)
    //這里主要是回收原來的growable_array,數(shù)組長(zhǎng)度置為0母怜,內(nèi)部data數(shù)組置為空數(shù)組
    growable_array.SetLength(0);
    growable_array.SetData(Object::empty_array());
    //注意: 定長(zhǎng)數(shù)組實(shí)現(xiàn)的關(guān)鍵點(diǎn)來了余耽,會(huì)調(diào)用Truncate方法將array截?cái)鄒sed_len長(zhǎng)度
    array.Truncate(used_len);
    return array.raw();//最后返回array.raw()
}
  • 總結(jié)一下

List.from的源碼實(shí)現(xiàn),首先傳入elements的Iterate<E>, 如果elements不帶泛型參數(shù)苹熏,也就是所謂的原生集合類型碟贾,并且是非unique,直接返回空數(shù)組; 如果帶泛型參數(shù)空集合轨域,那么會(huì)創(chuàng)建新的空集合并帶上原來泛型參數(shù)返回袱耽;如果是帶泛型參數(shù)非空集合,會(huì)取出其中data數(shù)組疙挺,來創(chuàng)建一個(gè)新的復(fù)制原來數(shù)據(jù)的集合并帶上原來泛型參數(shù)返回扛邑,最后需要截?cái)喟褦?shù)組截?cái)喑稍紨?shù)組長(zhǎng)度。

  • 為什么需要exteranl function

關(guān)鍵就是在于它能實(shí)現(xiàn)聲明和實(shí)現(xiàn)分離铐然,這樣就能復(fù)用同一套對(duì)外API的聲明蔬崩,然后對(duì)應(yīng)多套多平臺(tái)的實(shí)現(xiàn),如果對(duì)源碼感興趣的小伙伴就會(huì)發(fā)現(xiàn)相同API聲明在js中也有另一套實(shí)現(xiàn)搀暑,這樣不管是dart for web 還是dart for vm對(duì)于上層開發(fā)而言都是一套API沥阳,對(duì)于上層開發(fā)者是透明的。

二自点、Set

dart:core包中的Set集合實(shí)際上是委托到dart:collection中的LinkedHashSet來實(shí)現(xiàn)的桐罕。集合Set和列表List的區(qū)別在于 集合中的元素是不能重復(fù) 的。所以添加重復(fù)的元素時(shí)會(huì)返回false,表示添加不成功.

  • Set初始化方式
main() {
  Set<String> colorSet = {'red', 'yellow', 'blue', 'green'}; //直接使用{}形式初始化
  var colorList = <String>{'red', 'yellow', 'blue', 'green'};
}
  • 集合中的交桂敛、并功炮、補(bǔ)集,在Kotlin并沒有直接給到計(jì)算集合交术唬、并薪伏、補(bǔ)的API
main() {
  var colorSet1 = {'red', 'yellow', 'blue', 'green'};
  var colorSet2 = {'black', 'yellow', 'blue', 'green', 'white'};
  print(
      colorSet1.intersection(colorSet2)); //交集-->輸出: {'yellow', 'blue', 'green'}
  print(colorSet1.union(
      colorSet2)); //并集--->輸出: {'black', 'red', 'yellow', 'blue', 'green', 'white'}
  print(colorSet1.difference(colorSet2)); //補(bǔ)集--->輸出: {'red'}
}
  • Set的遍歷方式(和List一樣)
main() {
  Set<String> colorSet = {'red', 'yellow', 'blue', 'green'};
//for-i遍歷
  for (var i = 0; i < colorSet.length; i++) {
    //可以使用var或int
    print(colorSet[i]);
  }
  //forEach遍歷
  colorSet.forEach((color) => print(color)); //forEach的參數(shù)為Function. =>使用了箭頭函數(shù)
  //for-in遍歷
  for (var color in colorSet) {
    print(color);
  }
  //while+iterator迭代器遍歷,類似Java中的iteator
  while (colorSet.iterator.moveNext()) {
    print(colorSet.iterator.current);
  }
}
  • 構(gòu)造函數(shù)源碼分析
    //主構(gòu)造器委托到LinkedHashSet主構(gòu)造器 
    factory Set() = LinkedHashSet<E>; 
    //Set的命名構(gòu)造器identity委托給LinkedHashSet的identity
    factory Set.identity() = LinkedHashSet<E>.identity; 
    //Set的命名構(gòu)造器from委托給LinkedHashSet的from 
    factory Set.from(Iterable elements) = LinkedHashSet<E>.from;
    //Set的命名構(gòu)造器of委托給LinkedHashSet的of
    factory Set.of(Iterable<E> elements) = LinkedHashSet<E>.of;
  • 對(duì)應(yīng)LinkedHashSet的源碼分析,篇幅有限感興趣可以去深入研究
 abstract class LinkedHashSet implements Set { 
  //LinkedHashSet主構(gòu)造器聲明帶了三個(gè)函數(shù)類型參數(shù)作為可選參數(shù),同樣是通過exteranl實(shí)現(xiàn)聲明和實(shí)現(xiàn)分離粗仓,要深入可找到對(duì)應(yīng)的@Patch實(shí)現(xiàn)
 external factory LinkedHashSet( {bool equals(E e1, E e2), int hashCode(E e), bool isValidKey(potentialKey)});

//LinkedHashSet命名構(gòu)造器from   
factory LinkedHashSet.from(Iterable elements) {
  //內(nèi)部直接創(chuàng)建一個(gè)LinkedHashSet對(duì)象
  LinkedHashSet<E> result = LinkedHashSet<E>();
  //并將傳入elements元素遍歷加入到LinkedHashSet中
  for (final element in elements) {
    result.add(element);
  }
  return result;
}

//LinkedHashSet命名構(gòu)造器of嫁怀,首先創(chuàng)建一個(gè)LinkedHashSet對(duì)象设捐,通過級(jí)聯(lián)操作直接通過addAll方法將元素加入到elements
factory LinkedHashSet.of(Iterable<E> elements) =>
    LinkedHashSet<E>()..addAll(elements);

void forEach(void action(E element));

Iterator<E> get iterator;
} 
  • 對(duì)應(yīng)的 sdk/lib/_internal/vm/lib/collection_patch.dart 中的@Patch LinkedHashSet
@patch 
class LinkedHashSet { 
  @patch 
  factory LinkedHashSet( {bool equals(E e1, E e2), int hashCode(E e), bool isValidKey(potentialKey)}) {
    if (isValidKey == null) { 
        if (hashCode == null) {
          if (equals == null) { 
            return new _CompactLinkedHashSet(); //可選參數(shù)都為null,默認(rèn)創(chuàng)建_CompactLinkedHashSet
          } 
          hashCode = _defaultHashCode; 
        } else { 
          if (identical(identityHashCode, hashCode) && identical(identical, equals)) { 
            return new _CompactLinkedIdentityHashSet();//創(chuàng)建_CompactLinkedIdentityHashSet 
          }
          equals ??= _defaultEquals; 
        }
      } else { 
        hashCode ??= _defaultHashCode;
        equals ??= _defaultEquals; 
      } 
    return new _CompactLinkedCustomHashSet(equals, hashCode, isValidKey);//可選參數(shù)identical,默認(rèn)創(chuàng)建_CompactLinkedCustomHashSet 
  }

  @patch
  factory LinkedHashSet.identity() => new _CompactLinkedIdentityHashSet<E>();
} 

三、Map

dart:core 包中的 Map集合 實(shí)際上是 委托到dart:collection中的LinkedHashMap 來實(shí)現(xiàn)的塘淑。集合Map和Kotlin類似萝招,key-value形式存儲(chǔ),并且 Map對(duì)象的中key是不能重復(fù)的

  • Map初始化方式
main() {
  Map<String, int> colorMap = {
    'white': 0xffffffff,
    'black': 0xff000000
  }; //使用{key:value}形式初始化
  var colorMap = <String, int>{'white': 0xffffffff, 'black': 0xff000000};
  var colorMap = Map<String, int>(); //創(chuàng)建一個(gè)空的Map集合
//實(shí)際上等價(jià)于下面代碼存捺,后面會(huì)通過源碼說明
  var colorMap = LinkedHashMap<String, int>();
}
  • Map中常用的函數(shù)
main() {
  Map<String, int> colorMap = {'white': 0xffffffff, 'black': 0xff000000};
  print(colorMap.containsKey('green')); //false
  print(colorMap.containsValue(0xff000000)); //true
  print(colorMap.keys.toList()); //['white','black']
  print(colorMap.values.toList()); //[0xffffffff, 0xff000000]
  colorMap['white'] = 0xfffff000; //修改指定key的元素
  colorMap.remove('black'); //移除指定key的元素
}
  • Map的遍歷方式
main() {
  Map<String, int> colorMap = {'white': 0xffffffff, 'black': 0xff000000};
//for-each key-value
  colorMap
      .forEach((key, value) => print('color is $key, color value is $value'));
}
  • Map.fromIterables將List集合轉(zhuǎn)化成Map
main() {
  List<String> colorKeys = ['white', 'black'];
  List<int> colorValues = [0xffffffff, 0xff000000];
  Map<String, int> colorMap = Map.fromIterables(colorKeys, colorValues);
}
  • 構(gòu)造函數(shù)源碼分析
external factory Map(); //主構(gòu)造器交由外部@Patch實(shí)現(xiàn), 實(shí)際上對(duì)應(yīng)的@Patch實(shí)現(xiàn)還是委托給LinkedHashMap

factory Map.from(Map other) = LinkedHashMap.from;//Map的命名構(gòu)造器from委托給LinkedHashMap的from

factory Map.of(Map other) = LinkedHashMap.of;//Map的命名構(gòu)造器of委托給LinkedHashMap的of

external factory Map.unmodifiable(Map other);//unmodifiable構(gòu)造器交由外部@Patch實(shí)現(xiàn)

factory Map.identity() = LinkedHashMap.identity;//Map的命名構(gòu)造器identity交由外部@Patch實(shí)現(xiàn)

factory Map.fromIterable(Iterable iterable, {K key(element), V value(element)}) = LinkedHashMap.fromIterable;//Map的命名構(gòu)造器fromIterable委托給LinkedHashMap的fromIterable

factory Map.fromIterables(Iterable keys, Iterable values) = LinkedHashMap.fromIterables;//Map的命名構(gòu)造器fromIterables委托給LinkedHashMap的fromIterables
  • 對(duì)應(yīng)LinkedHashMap構(gòu)造函數(shù)源碼分析
abstract class LinkedHashMap implements Map { //主構(gòu)造器交由外部@Patch實(shí)現(xiàn) external factory LinkedHashMap( {bool equals(K key1, K key2), int hashCode(K key), bool isValidKey(potentialKey)});

//LinkedHashMap命名構(gòu)造器identity交由外部@Patch實(shí)現(xiàn)
external factory LinkedHashMap.identity();

//LinkedHashMap的命名構(gòu)造器from
factory LinkedHashMap.from(Map other) {
  //創(chuàng)建一個(gè)新的LinkedHashMap對(duì)象
  LinkedHashMap<K, V> result = LinkedHashMap<K, V>();
  //遍歷other中的元素槐沼,并添加到新的LinkedHashMap對(duì)象
  other.forEach((k, v) {
    result[k] = v;
  });
  return result;
}

//LinkedHashMap的命名構(gòu)造器of,創(chuàng)建一個(gè)新的LinkedHashMap對(duì)象,通過級(jí)聯(lián)操作符調(diào)用addAll批量添加map到新的LinkedHashMap中
factory LinkedHashMap.of(Map<K, V> other) =>
    LinkedHashMap<K, V>()..addAll(other);

//LinkedHashMap的命名構(gòu)造器fromIterable捌治,傳入的參數(shù)是iterable對(duì)象母赵、key函數(shù)參數(shù)、value函數(shù)參數(shù)兩個(gè)可選參數(shù)
factory LinkedHashMap.fromIterable(Iterable iterable,
    {K key(element), V value(element)}) {
  //創(chuàng)建新的LinkedHashMap對(duì)象具滴,通過MapBase中的static方法_fillMapWithMappedIterable凹嘲,給新的map添加元素  
  LinkedHashMap<K, V> map = LinkedHashMap<K, V>();
  MapBase._fillMapWithMappedIterable(map, iterable, key, value);
  return map;
}

//LinkedHashMap的命名構(gòu)造器fromIterables
factory LinkedHashMap.fromIterables(Iterable<K> keys, Iterable<V> values) {
//創(chuàng)建新的LinkedHashMap對(duì)象,通過MapBase中的static方法_fillMapWithIterables构韵,給新的map添加元素
  LinkedHashMap<K, V> map = LinkedHashMap<K, V>();
  MapBase._fillMapWithIterables(map, keys, values);
  return map;
}
}

//MapBase中的_fillMapWithMappedIterable
static void _fillMapWithMappedIterable( Map map, Iterable iterable, key(element), value(element)) { key ??= _id; value ??= _id;

for (var element in iterable) {//遍歷iterable周蹭,給map對(duì)應(yīng)復(fù)制
    map[key(element)] = value(element);
  }
}

// MapBase中的_fillMapWithIterables static void _fillMapWithIterables(Map map, Iterable keys, Iterable values) { Iterator keyIterator = keys.iterator;//拿到keys的iterator Iterator valueIterator = values.iterator;//拿到values的iterator

bool hasNextKey = keyIterator.moveNext();//是否有NextKey
  bool hasNextValue = valueIterator.moveNext();//是否有NextValue

  while (hasNextKey && hasNextValue) {//同時(shí)遍歷迭代keys,values
    map[keyIterator.current] = valueIterator.current;
    hasNextKey = keyIterator.moveNext();
    hasNextValue = valueIterator.moveNext();
  }

  if (hasNextKey || hasNextValue) {//最后如果其中只要有一個(gè)為true,說明key與value的長(zhǎng)度不一致疲恢,拋出異常
    throw ArgumentError("Iterables do not have same length.");
  }
}
  • Map的@Patch對(duì)應(yīng)實(shí)現(xiàn)凶朗,對(duì)應(yīng) sdk/lib/_internal/vm/lib/map_patch.dart 中
@patch 
class Map {
  @patch
  factory Map.unmodifiable(Map other) {
    return new UnmodifiableMapView(new Map.from(other));
  }

  @patch
  factory Map() => new LinkedHashMap<K, V>();
 //可以看到Map的創(chuàng)建實(shí)際上最終還是對(duì)應(yīng)創(chuàng)建了LinkedHashMap<K, V>
}

四、Queue

Queue隊(duì)列顧名思義先進(jìn)先出的一種數(shù)據(jù)結(jié)構(gòu)显拳,在Dart對(duì)隊(duì)列也做了一定的支持, 實(shí)際上Queue的實(shí)現(xiàn)是委托給ListQueue來實(shí)現(xiàn)棚愤。 Queue繼承于EfficientLengthIterable<E>接口,然后EfficientLengthIterable<E>接口又繼承了Iterable<E>.所以意味著Queue可以向List那樣使用豐富的操作函數(shù)杂数。并且由Queue派生出了 DoubleLinkedQueue和ListQueue

  • 初始化
import 'dart:collection'; //注意: Queue位于dart:collection包中需要導(dǎo)包

main() {
  //通過主構(gòu)造器初始化
  var queueColors = Queue();
  queueColors.addFirst('red');
  queueColors.addLast('yellow');
  queueColors.add('blue');

  //通過from命名構(gòu)造器初始化
  var queueColors2 = Queue.from(['red', 'yellow', 'blue']);
  //通過of命名構(gòu)造器初始化
  var queueColors3 = Queue.of(['red', 'yellow', 'blue']);
}
  • 常用的函數(shù)
import 'dart:collection'; //注意: Queue位于dart:collection包中需要導(dǎo)包

main() {
  var queueColors = Queue()
    ..addFirst('red')
    ..addLast('yellow')
    ..add('blue')
    ..addAll(['white', 'black'])
    ..remove('black')
    ..clear();
}
  • 遍歷
import 'dart:collection'; //注意: Queue位于dart:collection包中需要導(dǎo)包

main() {
  Queue colorQueue = Queue.from(['red', 'yellow', 'blue', 'green']);
//for-i遍歷
  for (var i = 0; i < colorQueue.length; i++) {
    //可以使用var或int
    print(colorQueue.elementAt(i));
    //注意: 獲取隊(duì)列中的元素不用使用colorQueue[i], 因?yàn)镼ueue內(nèi)部并沒有去實(shí)現(xiàn)[]運(yùn)算符重載
  }
  //forEach遍歷
  colorQueue.forEach((color) => print(color));
  //forEach的參數(shù)為Function. =>使用了箭頭函數(shù)
  //for-in遍歷
  for (var color in colorQueue) {
    print(color);
  }
}
  • 構(gòu)造函數(shù)源碼分析
factory Queue() = ListQueue;//委托給ListQueue主構(gòu)造器

factory Queue.from(Iterable elements) = ListQueue<E>.from;//委托給ListQueue<E>的命名構(gòu)造器from

factory Queue.of(Iterable<E> elements) = ListQueue<E>.of;//委托給ListQueue<E>的命名構(gòu)造器of
  • 對(duì)應(yīng)的ListQueue的源碼分析
class ListQueue extends ListIterable implements Queue { 
  static const int _INITIAL_CAPACITY = 8;//默認(rèn)隊(duì)列的初始化容量是8 
  List _table; 
  int _head; 
  int _tail; 
  int _modificationCount = 0;

  ListQueue([int? initialCapacity])
    : _head = 0,
      _tail = 0,
      _table = List<E?>(_calculateCapacity(initialCapacity));
//有趣的是可以看到ListQueque內(nèi)部實(shí)現(xiàn)是一個(gè)List<E?>集合, E?還是一個(gè)泛型類型為可空類型宛畦,但是目前dart的可空類型特性還在實(shí)驗(yàn)中,不過可以看到它的源碼中已經(jīng)用起來了揍移。

//計(jì)算隊(duì)列所需要容量大小
static int _calculateCapacity(int? initialCapacity) {
  //如果initialCapacity為null或者指定的初始化容量小于默認(rèn)的容量就是用默認(rèn)的容量大小
  if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) {

    return _INITIAL_CAPACITY;
  } else if (!_isPowerOf2(initialCapacity)) {//容量大小不是2次冪
    return _nextPowerOf2(initialCapacity);//找到大小是接近number的2次冪的數(shù)
  }
  assert(_isPowerOf2(initialCapacity));//斷言檢查
  return initialCapacity;//最終返回initialCapacity,返回的容量大小一定是2次冪的數(shù)
}

//判斷容量大小是否是2次冪
static bool _isPowerOf2(int number) => (number & (number - 1)) == 0;

//找到大小是接近number的二次冪的數(shù)
static int _nextPowerOf2(int number) {
  assert(number > 0);
  number = (number << 1) - 1;
  for (;;) {
    int nextNumber = number & (number - 1);
    if (nextNumber == 0) return number;
    number = nextNumber;
  }
}

//ListQueue的命名構(gòu)造函數(shù)from
factory ListQueue.from(Iterable<dynamic> elements) {
  //判斷elements 是否是List<dynamic>類型
  if (elements is List<dynamic>) {
    int length = elements.length;//取出長(zhǎng)度
    ListQueue<E> queue = ListQueue<E>(length + 1);//創(chuàng)建length + 1長(zhǎng)度的ListQueue
    assert(queue._table.length > length);//必須保證新創(chuàng)建的queue的長(zhǎng)度大于傳入elements的長(zhǎng)度
    for (int i = 0; i < length; i++) {
      queue._table[i] = elements[i] as E;//然后就是給新queue中的元素賦值次和,注意需要強(qiáng)轉(zhuǎn)成泛型類型E
    }
    queue._tail = length;//最終移動(dòng)隊(duì)列的tail尾部下標(biāo),因?yàn)榭赡艽嬖趯?shí)際長(zhǎng)度大于實(shí)際元素長(zhǎng)度
    return queue;
  } else {
    int capacity = _INITIAL_CAPACITY;
    if (elements is EfficientLengthIterable) {//如果是EfficientLengthIterable類型那伐,就將elements長(zhǎng)度作為初始容量不是就使用默認(rèn)容量
      capacity = elements.length;
    }
    ListQueue<E> result = ListQueue<E>(capacity);
    for (final element in elements) {
      result.addLast(element as E);//通過addLast從隊(duì)列尾部插入
    }
    return result;//最終返回result
  }
}

//ListQueue的命名構(gòu)造函數(shù)of
factory ListQueue.of(Iterable<E> elements) =>
    ListQueue<E>()..addAll(elements); //直接創(chuàng)建ListQueue<E>()并通過addAll把elements加入到新的ListQueue中
...
} 

五踏施、LinkedList

在dart中LinkedList比較特殊,它不是一個(gè)帶泛型集合罕邀,因?yàn)樗盒皖愋蜕辖缡荓inkedListEntry, 內(nèi)部的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)是一個(gè)雙鏈表畅形,鏈表的結(jié)點(diǎn)是LinkedListEntry的子類,且內(nèi)部維護(hù)了_next和_previous指針诉探。此外它并沒有實(shí)現(xiàn)List接口

  • 初始化
import 'dart:collection'; //注意: LinkedList位于dart:collection包中需要導(dǎo)包 
main() { 
  var linkedList = LinkedList>(); 
  var prevLinkedEntry = LinkedListEntryImpl(99); 
  var currentLinkedEntry = LinkedListEntryImpl(100); 
  var nextLinkedEntry = LinkedListEntryImpl(101); 
  linkedList.add(currentLinkedEntry); 
  currentLinkedEntry.insertBefore(prevLinkedEntry);//在當(dāng)前結(jié)點(diǎn)前插入一個(gè)新的結(jié)點(diǎn) 
  currentLinkedEntry.insertAfter(nextLinkedEntry);//在當(dāng)前結(jié)點(diǎn)后插入一個(gè)新的結(jié)點(diǎn) 
  linkedList.forEach((entry) => print('${entry.value}'));
}

//需要定義一個(gè)LinkedListEntry子類 
class LinkedListEntryImpl extends LinkedListEntry> { 
  final T value;

  LinkedListEntryImpl(this.value);

  @override
  String toString() {
    return "value is $value";
  }
}
  • 常用的函數(shù)
    currentLinkedEntry.insertBefore(prevLinkedEntry);//在當(dāng)前結(jié)點(diǎn)前插入一個(gè)新的結(jié)點(diǎn) 
    currentLinkedEntry.insertAfter(nextLinkedEntry);//在當(dāng)前結(jié)點(diǎn)后插入一個(gè)新的結(jié)點(diǎn) 
    currentLinkedEntry.previous;//獲取當(dāng)前結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn) 
    currentLinkedEntry.next;//獲取當(dāng)前結(jié)點(diǎn)的后一個(gè)結(jié)點(diǎn) 
    currentLinkedEntry.list;//獲取LinkedList 
    currentLinkedEntry.unlink();//把當(dāng)前結(jié)點(diǎn)entry從LinkedList中刪掉
  • 遍歷
  //forEach迭代 
  linkedList.forEach((entry) => print('${entry.value}')); 
  //for-i迭代 
  for (var i = 0; i < linkedList.length; i++) { 
    print('${linkedList.elementAt(i).value}'); 
  } 
  //for-in迭代 
  for (var element in linkedList) { 
    print('${element.value}'); 
  }

六日熬、HashMap

  • 初始化
import 'dart:collection'; //注意: HashMap位于dart:collection包中需要導(dǎo)包

main() {
  var hashMap = HashMap(); //通過HashMap主構(gòu)造器初始化
  hashMap['a'] = 1;
  hashMap['b'] = 2;
  hashMap['c'] = 3;
  var hashMap2 = HashMap.from(hashMap); //通過HashMap命名構(gòu)造器from初始化
  var hashMap3 = HashMap.of(hashMap); //通過HashMap命名構(gòu)造器of初始化
  var keys = ['a', 'b', 'c'];
  var values = [1, 2, 3];
  var hashMap4 =
      HashMap.fromIterables(keys, values); //通過HashMap命名構(gòu)造器fromIterables初始化

  hashMap2.forEach((key, value) => print('key: $key  value: $value'));
}

  • 常用的函數(shù)
import 'dart:collection'; //注意: HashMap位于dart:collection包中需要導(dǎo)包

main() {
  var hashMap = HashMap(); //通過HashMap主構(gòu)造器初始化
  hashMap['a'] = 1;
  hashMap['b'] = 2;
  hashMap['c'] = 3;
  print(hashMap.containsKey('a')); //false
  print(hashMap.containsValue(2)); //true
  print(hashMap.keys.toList()); //['a','b','c']
  print(hashMap.values.toList()); //[1, 2, 3]
  hashMap['a'] = 55; //修改指定key的元素
  hashMap.remove('b'); //移除指定key的元素
}
  • 遍歷
import 'dart:collection'; //注意: HashMap位于dart:collection包中需要導(dǎo)包

main() {
  var hashMap = HashMap(); //通過HashMap主構(gòu)造器初始化
  hashMap['a'] = 1;
  hashMap['b'] = 2;
  hashMap['c'] = 3;
  //for-each key-value
  hashMap.forEach((key, value) => print('key is $key, value is $value'));
}
  • 構(gòu)造函數(shù)源碼分析
  //主構(gòu)造器交由外部@Patch實(shí)現(xiàn) external 
  factory HashMap( {bool equals(K key1, K key2), int hashCode(K key), bool isValidKey(potentialKey)});

  //HashMap命名構(gòu)造器identity交由外部@Patch實(shí)現(xiàn) 
  external factory HashMap.identity();

  //HashMap命名構(gòu)造器from 
  factory HashMap.from(Map other) { 
    //創(chuàng)建一個(gè)HashMap對(duì)象 
    Map result = HashMap(); 
    //遍歷other集合并把元素賦值給新的HashMap對(duì)象 
    other.forEach((k, v) { 
      result[k] = v; 
    }); 
    return result; 
  }

  //HashMap命名構(gòu)造器of,把other添加到新創(chuàng)建HashMap對(duì)象 
  factory HashMap.of(Map other) => HashMap()..addAll(other);

  //HashMap命名構(gòu)造器fromIterable 
  factory HashMap.fromIterable(Iterable iterable, {K key(element), V value(element)}) { Map map = HashMap();//創(chuàng)建一個(gè)新的HashMap對(duì)象 MapBase._fillMapWithMappedIterable(map, iterable, key, value);//通過MapBase中的_fillMapWithMappedIterable賦值給新的HashMap對(duì)象 return map; }

  //HashMap命名構(gòu)造器fromIterables 
  factory HashMap.fromIterables(Iterable keys, Iterable values) { Map map = HashMap();//創(chuàng)建一個(gè)新的HashMap對(duì)象 MapBase._fillMapWithIterables(map, keys, values);//通過MapBase中的_fillMapWithIterables賦值給新的HashMap對(duì)象 return map; }
  • HashMap對(duì)應(yīng)的@Patch源碼實(shí)現(xiàn),sdk/lib/_internal/vm/lib/collection_patch.dart
@patch 
class HashMap { 
  @patch 
  factory HashMap( {bool equals(K key1, K key2), int hashCode(K key), bool isValidKey(potentialKey)}) { 
    if (isValidKey == null) { 
      if (hashCode == null) { 
        if (equals == null) { 
          return new _HashMap();//創(chuàng)建私有的_HashMap對(duì)象 
        } 
        hashCode = _defaultHashCode; 
      } else { 
        if (identical(identityHashCode, hashCode) && identical(identical, equals)) {
          return new _IdentityHashMap();//創(chuàng)建私有的_IdentityHashMap對(duì)象 
        } 
        equals ??= _defaultEquals; 
      } 
    } else { 
      hashCode ??= _defaultHashCode; 
      equals ??= _defaultEquals; 
    } 
    return new _CustomHashMap(equals, hashCode, isValidKey);//創(chuàng)建私有的_CustomHashMap對(duì)象 
  }

  @patch
  factory HashMap.identity() => new _IdentityHashMap<K, V>();

  Set<K> _newKeySet();
} 

七阵具、Map碍遍、HashMap、LinkedHashMap阳液、SplayTreeMap區(qū)別

在Dart中還有一個(gè)SplayTreeMap怕敬,它的初始化、常用的函數(shù)和遍歷方式和LinkedHashMap帘皿、HashMap使用類似东跪。但是Map、HashMap鹰溜、LinkedHashMap虽填、SplayTreeMap有什么區(qū)別呢。

  • Map
    Map是key-value鍵值對(duì)集合曹动。在Dart中的Map中的每個(gè)條目都可以迭代的斋日。迭代順序取決于HashMap,LinkedHashMap或SplayTreeMap的實(shí)現(xiàn)墓陈。如果您使用Map構(gòu)造函數(shù)創(chuàng)建實(shí)例恶守,則默認(rèn)情況下會(huì)創(chuàng)建一個(gè)LinkedHashMap。

  • HashMap
    HashMap不保證插入順序贡必。如果先插入key為A的元素兔港,然后再插入具有key為B的另一個(gè)元素,則在遍歷Map時(shí)仔拟,有可能先獲得元素B衫樊。

  • LinkedHashMap
    LinkedHashMap保證插入順序。根據(jù)插入順序?qū)Υ鎯?chǔ)在LinkedHashMap中的數(shù)據(jù)進(jìn)行排序利花。如果先插入key為A的元素科侈,然后再插入具有key為B的另一個(gè)元素,則在遍歷Map時(shí)炒事,總是先取的key為A的元素兑徘,然后再取的key為B的元素。

  • SplayTreeMap
    SplayTreeMap是一個(gè)自平衡二叉樹羡洛,它允許更快地訪問最近訪問的元素挂脑。基本操作如插入欲侮,查找和刪除可以在O(log(n))時(shí)間復(fù)雜度中完成崭闲。它通過使經(jīng)常訪問的元素靠近樹的根來執(zhí)行樹的旋轉(zhuǎn)。因此威蕉,如果需要更頻繁地訪問某些元素刁俭,則使用SplayTreeMap是一個(gè)不錯(cuò)的選擇。但是韧涨,如果所有元素的數(shù)據(jù)訪問頻率幾乎相同牍戚,則使用SplayTreeMap是沒有用的侮繁。

八、命名構(gòu)造函數(shù)from和of的區(qū)別以及使用建議

通過上述各個(gè)集合源碼可以看到如孝,基本上每個(gè)集合(List宪哩、Set、LinkedHashSet第晰、LinkedHashMap锁孟、Map、HashMap等)中都有from和of命名構(gòu)造函數(shù)茁瘦∑烦椋可能有的人有疑問了,它們有什么區(qū)別甜熔,各自的應(yīng)用場(chǎng)景呢圆恤。其實(shí)答案從源碼中就看出一點(diǎn)了。以List,Map中的from和of為例腔稀。

main() {
  var map = {'a': 1, 'b': 2, 'c': 3};
  var fromMap = Map.from(map); //返回類型是Map<dynamic, dynamic>
  var ofMap = Map.of(map); //返回類型是Map<String, int>

  var list = [1, 2, 3, 4];
  var fromList = List.from(list); //返回類型是List<dynamic>
  var ofList = List.of(list); //返回類型是List<int>
}

從上述例子可以看出List哑了、Map中的from函數(shù)返回對(duì)應(yīng)的集合泛型類型是 List<dynamic> 和 Map<dynamic, dynamic> 而of函數(shù)返回對(duì)應(yīng)集合泛型類型實(shí)際類型是 List<int> 和 Map<String, int>。我們都知道dynamic是一種無法確定的類型烧颖,在編譯期不檢查類型弱左,只在運(yùn)行器檢查類型,而具體類型是在編譯期檢查類型炕淮。而且從源碼中可以看到 from函數(shù)往往會(huì)處理比較復(fù)雜邏輯比如需要重新遍歷傳入的集合然后把元素加入到新的集合中拆火,而of函數(shù)只需要?jiǎng)?chuàng)建一個(gè)新的對(duì)象通過addAll函數(shù)批量添加傳入的集合元素。

所以這里為了代碼效率考慮給出建議是: 如果你傳入的原有集合元素類型是確定的涂圆,請(qǐng)盡量使用of函數(shù)創(chuàng)建新的集合们镜,否則就可以考慮使用from函數(shù)。

總結(jié)

到這里我們dart語法系列第二篇就結(jié)束了润歉,相信通過這篇文章大家對(duì)dart中的集合應(yīng)該有了全面的了解模狭,下面我們將繼續(xù)研究dart和Flutter相關(guān)內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踩衩,一起剝皮案震驚了整個(gè)濱河市嚼鹉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驱富,老刑警劉巖锚赤,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異褐鸥,居然都是意外死亡线脚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浑侥,“玉大人姊舵,你說我怎么就攤上這事≡⒙洌” “怎么了括丁?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)零如。 經(jīng)常有香客問我,道長(zhǎng)锄弱,這世上最難降的妖魔是什么考蕾? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮会宪,結(jié)果婚禮上肖卧,老公的妹妹穿的比我還像新娘。我一直安慰自己掸鹅,他們只是感情好塞帐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巍沙,像睡著了一般葵姥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上句携,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天榔幸,我揣著相機(jī)與錄音,去河邊找鬼矮嫉。 笑死削咆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蠢笋。 我是一名探鬼主播拨齐,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼昨寞!你這毒婦竟也來了瞻惋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤援岩,失蹤者是張志新(化名)和其女友劉穎熟史,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窄俏,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹂匹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凹蜈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片限寞。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忍啸,死狀恐怖薄湿,靈堂內(nèi)的尸體忽然破棺而出融撞,到底是詐尸還是另有隱情,我是刑警寧澤训唱,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布玫霎,位于F島的核電站凿滤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庶近。R本人自食惡果不足惜翁脆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鼻种。 院中可真熱鬧反番,春花似錦、人聲如沸叉钥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽投队。三九已至枫疆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敷鸦,已是汗流浹背养铸。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轧膘,地道東北人钞螟。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谎碍,于是被迫代替她去往敵國(guó)和親鳞滨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容