kotlin 添加第一个 集合_Flutter开发必学Dart语法篇之集合操作符函数与源码分析...
簡(jiǎn)述:
在上一篇文章中,我們?nèi)娴胤治隽顺S眉系氖褂靡约凹喜糠衷创a的分析。那么這一節(jié)講點(diǎn)更實(shí)用的內(nèi)容,絕對(duì)可以提高你的Flutter開發(fā)效率的函數(shù),那就是集合中常用的操作符函數(shù)。這次說的內(nèi)容的比較簡(jiǎn)單就是怎么用,以及源碼內(nèi)部是怎么實(shí)現(xiàn)的。
一、`Iterable`
在dart中幾乎所有集合擁有的操作符函數(shù)(例如: map、every、where、reduce等)都是因?yàn)槔^承或者實(shí)現(xiàn)了Iterable。
1、Iterable類關(guān)系圖
二、forEach
1、介紹
void forEach(void f(E element))forEach在dart中用于遍歷和迭代集合,也是dart中操作集合最常用的方法之一。接收一個(gè)f(E element)函數(shù)作為參數(shù),返回值類型為空void.
2、使用方式
main() { var languages = ['Dart', 'Kotlin', 'Java', 'Javascript', 'Go', 'Python', 'Swift']; languages.forEach((language) => print('The language is $language'));//由于只有一個(gè)表達(dá)式,所以可以直接使用箭頭函數(shù)。 languages.forEach((language){ if(language == 'Dart' || language == 'Kotlin') { print('My favorite language is $language'); } });}3、源碼解析
void forEach(void f(E element)) { //可以看到在forEach內(nèi)部實(shí)際上就是利用for-in迭代,每迭代一次就執(zhí)行一次f函數(shù), //并把當(dāng)前element回調(diào)出去 for (E element in this) f(element); }三、map
1、介紹
Iterable map(T f(E e))map函數(shù)主要用于集合中元素的映射,也可以映射轉(zhuǎn)化成其他類型的元素??梢钥吹絤ap接收一個(gè)T f(E e)函數(shù)作為參數(shù),最后返回一個(gè)泛型參數(shù)為T的Iterable。實(shí)際上是返回了帶有元素的一個(gè)新的惰性Iterable, 然后通過迭代的時(shí)候,對(duì)每個(gè)元素都調(diào)用f函數(shù)。注意: f函數(shù)是一個(gè)接收泛型參數(shù)為E的元素,然后返回一個(gè)泛型參數(shù)為T的元素,這就是map可以將原集合中每個(gè)元素映射成其他類型元素的原因。
2、使用方式
main() { var languages = ['Dart', 'Kotlin', 'Java', 'Javascript', 'Go', 'Python', 'Swift']; print(languages.map((language) => 'develop language is ${language}').join('---')); }3、源碼解析
以上面的例子為例,
- 1、首先,需要明確一點(diǎn),languages內(nèi)部本質(zhì)是一個(gè)_GrowableList, 我們都知道_GrowableList是繼承了ListBase,然后ListBase又mixin with ListMixin.所以languages.map函數(shù)調(diào)用就是調(diào)用ListMixin中的map函數(shù),實(shí)際上還是相當(dāng)于調(diào)用了自身的成員函數(shù)map.
以上面的例子為例,
- 1、首先,需要明確一點(diǎn),languages內(nèi)部本質(zhì)是一個(gè)_GrowableList, 我們都知道_GrowableList是繼承了ListBase,然后ListBase又mixin with ListMixin.所以languages.map函數(shù)調(diào)用就是調(diào)用ListMixin中的map函數(shù),實(shí)際上還是相當(dāng)于調(diào)用了自身的成員函數(shù)map.
- 3、為什么是惰性的呢,可以看到它并不是直接返回轉(zhuǎn)化后的集合,而是返回一個(gè)帶有值的MappedListIterable的,如果不執(zhí)行`elementAt`方法,是不會(huì)觸發(fā)執(zhí)行map傳入的`f`函數(shù), 所以它是惰性的。
- 4、一般不會(huì)單獨(dú)使用map函數(shù),因?yàn)閱为?dú)使用map的函數(shù)時(shí),僅僅返回的是惰性的MappedListIterable。由上面的源碼可知,僅僅在elementAt調(diào)用的時(shí)候才會(huì)觸發(fā)map中的閉包。所以我們一般使用完map后會(huì)配合toList()、toSet()函數(shù)或者觸發(fā)elementAt函數(shù)的函數(shù)(例如這里的join)一起使用。
四、any
1、介紹
bool any(bool test(E element))any函數(shù)主要用于檢查是否存在任意一個(gè)滿足條件的元素,只要匹配到第一個(gè)就返回true, 如果遍歷所有元素都不符合才返回false.
any函數(shù)接收一個(gè)bool test(E element)函數(shù)作為參數(shù),test函數(shù)回調(diào)一個(gè)E類型的element并返回一個(gè)bool類型的值。
2、使用方式
main() { bool isDartExisted = languages.any((language) => language == 'Dart');}3、源碼解析
bool any(bool test(E element)) { int length = this.length;//獲取到原集合的length //遍歷原集合,只要找到符合test函數(shù)的條件,就返回true for (int i = 0; i < length; i++) { if (test(this[i])) return true; if (length != this.length) { throw ConcurrentModificationError(this); } } //遍歷完集合后,未找到符合條件的集合就返回false return false; }五、every
1、介紹
bool every(bool test(E element))every函數(shù)主要用于檢查是否集合所有元素都滿足條件,如果都滿足就返回true, 只要存在一個(gè)不滿足條件的就返回false.
every函數(shù)接收一個(gè)bool test(E element)函數(shù)作為參數(shù),test函數(shù)回調(diào)一個(gè)E類型的element并返回一個(gè)bool類型的值。
2、使用方式
main() { bool isDartAll = languages.every((language) => language == 'Dart');}3、源碼解析
bool every(bool test(E element)) { //利用for-in遍歷集合,只要找到不符合test函數(shù)的條件,就返回false. for (E element in this) { if (!test(element)) return false; }//遍歷完集合后,找到所有元素符合條件就返回true return true; }六、where
1、介紹
Iterable where(bool test(E element))where函數(shù)主要用于過濾符合條件的元素,類似Kotlin中的filter的作用,最后返回符合條件元素的集合。
where函數(shù)接收一個(gè)bool test(E element)函數(shù)作為參數(shù),最后返回一個(gè)泛型參數(shù)為E的Iterable。類似map一樣,where這里也是返回一個(gè)惰性的Iterable, 然后對(duì)它的iterator進(jìn)行迭代,對(duì)每個(gè)元素都執(zhí)行test方法。
2、使用方式
main() { List numbers = [0, 3, 1, 2, 7, 12, 2, 4]; print(numbers.where((num) => num > 6));//輸出: (7,12) //注意: 這里是print的內(nèi)容實(shí)際上輸出的是Iterable的toString方法返回的內(nèi)容。}3、源碼解析
- 1、首先,需要明確一點(diǎn)numbers實(shí)際上是一個(gè)_GrowableList和map的分析原理類似,最終還是調(diào)用了ListMixin中的where函數(shù)。
- 2、然后,繼續(xù)深入研究下WhereIterable是如何實(shí)現(xiàn)的
- 3、然后,繼續(xù)深入研究下WhereIterator是如何實(shí)現(xiàn)的
- 4、一般在使用的WhereIterator的時(shí)候,外部肯定還有一層while迭代,但是這個(gè)WhereIterator比較特殊,moveNext()的返回值由where中閉包函數(shù)參數(shù)返回值決定的,符合條件元素moveNext()就返回true,不符合就略過,迭代檢查下一個(gè)元素,直至整個(gè)集合迭代完畢,moveNext()返回false,以此也就終止了外部的迭代循環(huán)。
- 5、上面分析,WhereIterable是惰性的,那它啥時(shí)候觸發(fā)呢? 沒錯(cuò)就是在迭代它的iterator時(shí)候才會(huì)觸發(fā),以上面例子為例
七、firstWhere和singleWhere和lastWhere
1、介紹
E firstWhere(bool test(E element), {E orElse()})E lastWhere(bool test(E element), {E orElse()})E singleWhere(bool test(E element), {E orElse()})首先從源碼聲明結(jié)構(gòu)上來看,firstWhere、lastWhere和singleWhere是一樣,它們都是接收兩個(gè)參數(shù),一個(gè)是必需參數(shù):test篩選條件閉包函數(shù),另一個(gè)是可選參數(shù):orElse閉包函數(shù)。
但是它們用法卻不同,firstWhere主要是用于篩選順序第一個(gè)符合條件的元素,可能存在多個(gè)符合條件元素;lastWhere主要是用于篩選順序最后一個(gè)符合條件的元素,可能存在多個(gè)符合條件元素;singleWhere主要是用于篩選順序唯一一個(gè)符合條件的元素,不可能存在多個(gè)符合條件元素,存在的話就會(huì)拋出異常IterableElementError.tooMany(), 所以使用它的使用需要謹(jǐn)慎注意下
2、使用方式
main() { var numbers = [0, 3, 1, 2, 7, 12, 2, 4]; //注意: 如果沒有找到,執(zhí)行orElse代碼塊,可返回一個(gè)指定的默認(rèn)值-1 print(numbers.firstWhere((num) => num == 5, orElse: () => -1)); //注意: 如果沒有找到,執(zhí)行orElse代碼塊,可返回一個(gè)指定的默認(rèn)值-1 print(numbers.lastWhere((num) => num == 2, orElse: () => -1)); //注意: 如果沒有找到,執(zhí)行orElse代碼塊,可返回一個(gè)指定的默認(rèn)值,前提是集合中只有一個(gè)符合條件的元素, 否則就會(huì)拋出異常 print(numbers.singleWhere((num) => num == 4, orElse: () => -1)); }3、源碼解析
//firstWhere E firstWhere(bool test(E element), {E orElse()}) { for (E element in this) {//直接遍歷原集合,只要找到第一個(gè)符合條件的元素就直接返回,終止函數(shù) if (test(element)) return element; } if (orElse != null) return orElse();//遍歷完集合后,都沒找到符合條件的元素并且外部傳入了orElse就會(huì)觸發(fā)orElse函數(shù) //否則找不到元素,直接拋出異常。所以這里需要注意下,如果不想拋出異常,可能你需要處理下orElse函數(shù)。 throw IterableElementError.noElement(); } //lastWhere E lastWhere(bool test(E element), {E orElse()}) { E result;//定義result來記錄每次符合條件的元素 bool foundMatching = false;//定義一個(gè)標(biāo)志位是否找到符合匹配的。 for (E element in this) { if (test(element)) {//每次找到符合條件的元素,都會(huì)重置result,所以result記錄了最新的符合條件元素,那么遍歷到最后,它也就是最后一個(gè)符合條件的元素 result = element; foundMatching = true;//找到后重置標(biāo)記位 } } if (foundMatching) return result;//如果標(biāo)記位為true直接返回result即可 if (orElse != null) return orElse();//處理orElse函數(shù) //同樣,找不到元素,直接拋出異常??赡苣阈枰幚硐耾rElse函數(shù)。 throw IterableElementError.noElement(); } //singleWhere E singleWhere(bool test(E element), {E orElse()}) { E result; bool foundMatching = false; for (E element in this) { if (test(element)) { if (foundMatching) {//主要注意這里,只要foundMatching為true,說明已經(jīng)找到一個(gè)符合條件的元素,如果觸發(fā)這條邏輯分支,說明不止一個(gè)元素符合條件就直接拋出IterableElementError.tooMany()異常 throw IterableElementError.tooMany(); } result = element; foundMatching = true; } } if (foundMatching) return result; if (orElse != null) return orElse(); //同樣,找不到元素,直接拋出異常。可能你需要處理下orElse函數(shù)。 throw IterableElementError.noElement(); }八、join
1、介紹
String join([String separator = ""])join函數(shù)主要是用于將集合所有元素值轉(zhuǎn)化成字符串,中間用指定的separator連接符連接。
可以看到j(luò)oin函數(shù)比較簡(jiǎn)單,接收一個(gè)separator分隔符的可選參數(shù),可選參數(shù)默認(rèn)值是空字符串,最后返回一個(gè)字符串。
2、使用方式
main() { var numbers = [0, 3, 1, 2, 7, 12, 2, 4]; print(numbers.join('-'));//輸出: 0-3-1-2-7-12-2-4}3、源碼解析
//接收separator可選參數(shù),默認(rèn)值為"" String join([String separator = ""]) { Iterator iterator = this.iterator; if (!iterator.moveNext()) return ""; //創(chuàng)建StringBuffer StringBuffer buffer = StringBuffer(); //如果分隔符為空或空字符串 if (separator == null || separator == "") { //do-while遍歷iterator,然后直接拼接元素 do { buffer.write("${iterator.current}"); } while (iterator.moveNext()); } else { //如果分隔符不為空 //先加入第一個(gè)元素 buffer.write("${iterator.current}"); //然后while遍歷iterator while (iterator.moveNext()) { buffer.write(separator);//先拼接分隔符 buffer.write("${iterator.current}");//再拼接元素 } } return buffer.toString();//最后返回最終的字符串。 }九、take
1、介紹
Iterable take(int count)take函數(shù)主要是用于截取原集合前count個(gè)元素組成的集合,take函數(shù)接收一個(gè)count作為函數(shù)參數(shù),最后返回一個(gè)泛型參數(shù)為E的Iterable。類似where一樣,take這里也是返回一個(gè)惰性的Iterable, 然后對(duì)它的iterator進(jìn)行迭代。
takeWhile函數(shù)主要用于
2、使用方式
main() { List numbers = [0, 3, 1, 2, 7, 12, 2, 4]; print(numbers.take(5));//輸出(0, 3, 1, 2, 7)}3、源碼解析
- 1、首先, 需要明確一點(diǎn)numbers.take調(diào)用了ListMixin中的take函數(shù),可以看到并沒有直接返回集合前count個(gè)元素,而是返回一個(gè)TakeIterable惰性Iterable。
- 2、然后,繼續(xù)深入研究TakeIterable
- 3、然后,繼續(xù)深入研究TakeIterator.
- 4、所以上述例子中最終還是調(diào)用Iterable的toString方法,方法中會(huì)進(jìn)行iterator的迭代,最終會(huì)觸發(fā)惰性TakeIterable中的TakeIterator的moveNext方法。
十、takeWhile
1、介紹
Iterable takeWhile(bool test(E value))takeWhile函數(shù)主要用于依次選擇滿足條件的元素,直到遇到第一個(gè)不滿足的元素,并停止選擇。takeWhile函數(shù)接收一個(gè)test條件函數(shù)作為函數(shù)參數(shù),然后返回一個(gè)惰性的Iterable。
2、使用方式
main() { List numbers = [3, 1, 2, 7, 12, 2, 4]; print(numbers.takeWhile((number) => number > 2).toList());//輸出: [3] 遇到1的時(shí)候就不滿足大于2條件就終止篩選。}3、源碼解析
- 1、首先,因?yàn)閚umbers是List所以還是調(diào)用ListMixin中的takeWhile函數(shù)
- 2、然后,繼續(xù)看下TakeWhileIterable的實(shí)現(xiàn)
十、skip
1、介紹
Iterable skip(int count)skip函數(shù)主要是用于跳過原集合前count個(gè)元素后,剩下元素組成的集合,skip函數(shù)接收一個(gè)count作為函數(shù)參數(shù),最后返回一個(gè)泛型參數(shù)為E的Iterable。類似where一樣,skip這里也是返回一個(gè)惰性的Iterable, 然后對(duì)它的iterator進(jìn)行迭代。
2、使用方式
main() { List numbers = [3, 1, 2, 7, 12, 2, 4]; print(numbers.skip(2).toList());//輸出: [2, 7, 12, 2, 4] 跳過前兩個(gè)元素3,1 直接從第3個(gè)元素開始 }3、源碼解析
- 1、首先,因?yàn)閚umbers是List所以還是調(diào)用ListMixin中的skip函數(shù)
- 2、然后,繼續(xù)看下SubListIterable的實(shí)現(xiàn),這里只看下elementAt函數(shù)實(shí)現(xiàn)
十一、skipWhile
1、介紹
Iterable skipWhile(bool test(E element))skipWhile函數(shù)主要用于依次跳過滿足條件的元素,直到遇到第一個(gè)不滿足的元素,并停止篩選。skipWhile函數(shù)接收一個(gè)test條件函數(shù)作為函數(shù)參數(shù),然后返回一個(gè)惰性的Iterable。
2、使用方式
main() { List numbers = [3, 1, 2, 7, 12, 2, 4]; print(numbers.skipWhile((number) => number < 4).toList());//輸出: [7, 12, 2, 4] //因?yàn)?、1、2都是滿足小于4的條件,所以直接skip跳過,直到遇到7不符合條件停止篩選,剩下的就是[7, 12, 2, 4]}3、源碼解析
- 1、首先,因?yàn)閚umbers是List所以還是調(diào)用ListMixin中的skipWhile函數(shù)
- 2、然后,繼續(xù)看下SkipWhileIterable的實(shí)現(xiàn)
十二、follwedBy
1、介紹
Iterable followedBy(Iterable other)followedBy函數(shù)主要用于在原集合后面追加拼接另一個(gè)Iterable集合,followedBy函數(shù)接收一個(gè)Iterable參數(shù),最后又返回一個(gè)Iterable類型的值。
2、使用方式
main() { var languages = ['Kotlin', 'Java', 'Dart', 'Go', 'Python']; print(languages.followedBy(['Swift', 'Rust', 'Ruby', 'C++', 'C#']).toList());//輸出: [Kotlin, Java, Dart, Go, Python, Swift, Rust, Ruby, C++, C#]}3、源碼解析
- 1、首先,還是調(diào)用ListMixin中的followedBy函數(shù)
- 2、然后,繼續(xù)看下FollowedByIterable中的firstEfficient實(shí)現(xiàn)
- 3、然后,繼續(xù)看下EfficientLengthFollowedByIterable的實(shí)現(xiàn),這里只具體看下elementAt函數(shù)的實(shí)現(xiàn)
十三、expand
1、介紹
Iterable expand(Iterable f(E element))expand函數(shù)主要用于將集合中每個(gè)元素?cái)U(kuò)展為零個(gè)或多個(gè)元素或者將多個(gè)元素組成二維數(shù)組展開成平鋪一個(gè)一維數(shù)組。expand函數(shù)接收一個(gè)Iterable f(E element)函數(shù)作為函數(shù)參數(shù)。這個(gè)閉包函數(shù)比較特別,特別之處在于f函數(shù)返回的是一個(gè)Iterable,那么就意味著可以將原集合中每個(gè)元素?cái)U(kuò)展成多個(gè)相同元素。注意expand函數(shù)最終還是返回一個(gè)惰性的Iterable
2、使用方式
main() { var pair = [ [1, 2], [3, 4] ]; print('flatten list: ${pair.expand((pair) => pair).toList()}');//輸出: flatten list: [1, 2, 3, 4] var inputs = [1, 2, 3]; print('duplicated list: ${inputs.expand((number) => [number, number, number]).toList()}');//輸出: duplicated list: [1, 1, 1, 2, 2, 2, 3, 3, 3]}3、源碼解析
- 1、首先還是調(diào)用ListMixin中的expand函數(shù)。
- 2、然后繼續(xù)深入ExpandIterable
十四、reduce
1、介紹
E reduce(E combine(E previousValue, E element))T fold(T initialValue, T combine(T previousValue, E element))reduce函數(shù)主要用于集合中元素依次歸納(combine),每次歸納后的結(jié)果會(huì)和下一個(gè)元素進(jìn)行歸納,它可以用來累加或累乘,具體取決于combine函數(shù)中操作,combine函數(shù)中會(huì)回調(diào)上一次歸納后的值和當(dāng)前元素值,reduce提供的是獲取累積迭代結(jié)果的便利條件. fold和reduce幾乎相同,唯一區(qū)別是fold可以指定初始值。 但是需要注意的是,combine函數(shù)返回值的類型必須和集合泛型類型一致。
2、使用方式
main() { List numbers = [3, 1, 2, 7, 12, 2, 4]; print(numbers.reduce((prev, curr) => prev + curr)); //累加 print(numbers.fold(2, (prev, curr) => (prev as int) + curr)); //累加 print(numbers.reduce((prev, curr) => prev + curr) / numbers.length); //求平均數(shù) print(numbers.fold(2, (prev, curr) => (prev as int) + curr) / numbers.length); //求平均數(shù) print(numbers.reduce((prev, curr) => prev * curr)); //累乘 print(numbers.fold(2, (prev, curr) => (prev as int) * curr)); //累乘 var strList = ['a', 'b', 'c']; print(strList.reduce((prev, curr) => '$prev*$curr')); //拼接字符串 print(strList.fold('e', (prev, curr) => '$prev*$curr')); //拼接字符串}3、源碼解析
E reduce(E combine(E previousValue, E element)) { int length = this.length; if (length == 0) throw IterableElementError.noElement(); E value = this[0];//初始值默認(rèn)取第一個(gè) for (int i = 1; i < length; i++) {//從第二個(gè)開始遍歷 value = combine(value, this[i]);//combine回調(diào)value值和當(dāng)前元素值,然后把combine的結(jié)果歸納到value上,依次處理。 if (length != this.length) { throw ConcurrentModificationError(this);//注意: 在操作過程中不允許刪除和添加元素否則就會(huì)出現(xiàn)ConcurrentModificationError } } return value; } T fold(T initialValue, T combine(T previousValue, E element)) { var value = initialValue;//和reduce唯一區(qū)別在于這里value初始值是外部指定的 int length = this.length; for (int i = 0; i < length; i++) { value = combine(value, this[i]); if (length != this.length) { throw ConcurrentModificationError(this); } } return value; }十五、elementAt
1、介紹
E elementAt(int index)elementAt函數(shù)用于獲取對(duì)應(yīng)index下標(biāo)的元素,傳入一個(gè)index參數(shù),返回對(duì)應(yīng)泛型類型E的元素。
2、使用方式
main() { print(numbers.elementAt(3));//elementAt一般不會(huì)直接使用,更多是使用[],運(yùn)算符重載的方式間接使用。 }3、源碼解析
E elementAt(int index) { ArgumentError.checkNotNull(index, "index"); RangeError.checkNotNegative(index, "index"); int elementIndex = 0; //for-in遍歷原集合,找到對(duì)應(yīng)elementIndex元素并返回 for (E element in this) { if (index == elementIndex) return element; elementIndex++; } //找不到拋出RangeError throw RangeError.index(index, this, "index", null, elementIndex); }總結(jié)
到這里,有關(guān)dart中集合操作符函數(shù)相關(guān)內(nèi)容就結(jié)束了,關(guān)于集合操作符函數(shù)使用在Flutter中開發(fā)非常有幫助,特別在處理集合數(shù)據(jù)中,可以讓你的代碼實(shí)現(xiàn)更優(yōu)雅,不要再是一上來就for循環(huán)直接開干,雖然也能實(shí)現(xiàn),但是如果能適當(dāng)使用操作符函數(shù),將會(huì)使代碼更加簡(jiǎn)潔。歡迎繼續(xù)關(guān)注,下一篇Dart中的函數(shù)的使用…
總結(jié)
以上是生活随笔為你收集整理的kotlin 添加第一个 集合_Flutter开发必学Dart语法篇之集合操作符函数与源码分析...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蓝点linux_新闻速读 gt; Win
- 下一篇: gateway 过滤器执行顺序_Gate