kotlin 添加第一个 集合_Flutter开发必学Dart语法篇之集合操作符函数与源码分析...

简述:

在上一篇文章中,我们全面地分析了常用集合的使用以及集合部分源码的分析。那么这一节讲点更实用的内容,绝对可以提高你的Flutter开发效率的函数,那就是集合中常用的操作符函数。这次说的内容的比较简单就是怎么用,以及源码内部是怎么实现的。

一、`Iterable`

在dart中几乎所有集合拥有的操作符函数(例如: map、every、where、reduce等)都是因为继承或者实现了Iterable。

1、Iterable类关系图

c9df15d108b3d678f0ab4e21e785edd7.png

二、forEach

1、介绍

 void forEach(void f(E element))

forEach在dart中用于遍历和迭代集合,也是dart中操作集合最常用的方法之一。接收一个f(E element)函数作为参数,返回值类型为空void.

2、使用方式

main() { var languages = ['Dart', 'Kotlin', 'Java', 'Javascript', 'Go', 'Python', 'Swift']; languages.forEach((language) => print('The language is $language'));//由于只有一个表达式,所以可以直接使用箭头函数。 languages.forEach((language){ if(language == 'Dart' || language == 'Kotlin') { print('My favorite language is $language'); }  });} 

3、源码解析

 void forEach(void f(E element)) { //可以看到在forEach内部实际上就是利用for-in迭代,每迭代一次就执行一次f函数, //并把当前element回调出去 for (E element in this) f(element); }

三、map

1、介绍

Iterable map(T f(E e))

map函数主要用于集合中元素的映射,也可以映射转化成其他类型的元素。可以看到map接收一个T f(E e)函数作为参数,最后返回一个泛型参数为T的Iterable。实际上是返回了带有元素的一个新的惰性Iterable, 然后通过迭代的时候,对每个元素都调用f函数。注意: f函数是一个接收泛型参数为E的元素,然后返回一个泛型参数为T的元素,这就是map可以将原集合中每个元素映射成其他类型元素的原因。

2、使用方式

main() { var languages = ['Dart', 'Kotlin', 'Java', 'Javascript', 'Go', 'Python', 'Swift']; print(languages.map((language) => 'develop language is ${language}').join('---')); }

3、源码解析

以上面的例子为例,

  • 1、首先,需要明确一点,languages内部本质是一个_GrowableList, 我们都知道_GrowableList是继承了ListBase,然后ListBase又mixin with ListMixin.所以languages.map函数调用就是调用ListMixin中的map函数,实际上还是相当于调用了自身的成员函数map.

以上面的例子为例,

  • 1、首先,需要明确一点,languages内部本质是一个_GrowableList, 我们都知道_GrowableList是继承了ListBase,然后ListBase又mixin with ListMixin.所以languages.map函数调用就是调用ListMixin中的map函数,实际上还是相当于调用了自身的成员函数map.
@pragma("vm:entry-point")class _GrowableList extends ListBase {//_GrowableList是继承了ListBase ...}abstract class ListBase extends Object with ListMixin {//ListBase mixin with ListMixin ...}
2、然后可以看到ListMixin实际上实现了List,然后List继承了EfficientLengthIterable,最后EfficientLengthIterable继承Iterable,所以最终的map函数来自于Iterable但是具体的实现定义在ListMinxin中。
abstract class ListMixin implements List { ... //可以看到这里是直接返回一个MappedListIterable,它是一个惰性Iterable Iterable map(T f(E element)) => MappedListIterable(this, f); ... }
  • 3、为什么是惰性的呢,可以看到它并不是直接返回转化后的集合,而是返回一个带有值的MappedListIterable的,如果不执行`elementAt`方法,是不会触发执行map传入的`f`函数, 所以它是惰性的。
class MappedListIterable extends ListIterable { final Iterable _source;//_source存储了所携带的原集合 final _Transformation _f;//_f函数存储了map函数传入的闭包, MappedListIterable(this._source, this._f); int get length => _source.length; //注意: 只有elementAt函数执行的时候,才会触发执行_f方法,然后通过_source的elementAt函数取得原集合中的元素, //最后针对_source中的每个元素执行_f函数处理。 T elementAt(int index) => _f(_source.elementAt(index));}
  • 4、一般不会单独使用map函数,因为单独使用map的函数时,仅仅返回的是惰性的MappedListIterable。由上面的源码可知,仅仅在elementAt调用的时候才会触发map中的闭包。所以我们一般使用完map后会配合toList()、toSet()函数或者触发elementAt函数的函数(例如这里的join)一起使用。
languages.map((language) => 'develop language is ${language}').toList();//toList()方法调用才会真正去执行map中的闭包。languages.map((language) => 'develop language is ${language}').toSet();//toSet()方法调用才会真正去执行map中的闭包。languages.map((language) => 'develop language is ${language}').join('---');//join()方法调用才会真正去执行map中的闭包。 List toList({bool growable = true}) { List result; if (growable) { result = []..length = length; } else { result = List(length); } for (int i = 0; i < length; i++) { result[i] = this[i];//注意: 这里的this[i]实际上是运算符重载了[],最终就是调用了elementAt函数,这里才会真正的触发map中的闭包, } return result; }

四、any

1、介绍

 bool any(bool test(E element))

any函数主要用于检查是否存在任意一个满足条件的元素,只要匹配到第一个就返回true, 如果遍历所有元素都不符合才返回false.
any函数接收一个bool test(E element)函数作为参数,test函数回调一个E类型的element并返回一个bool类型的值。

2、使用方式

main() { bool isDartExisted = languages.any((language) => language == 'Dart');}

3、源码解析

 bool any(bool test(E element)) { int length = this.length;//获取到原集合的length //遍历原集合,只要找到符合test函数的条件,就返回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函数主要用于检查是否集合所有元素都满足条件,如果都满足就返回true, 只要存在一个不满足条件的就返回false.
every函数接收一个bool test(E element)函数作为参数,test函数回调一个E类型的element并返回一个bool类型的值。

2、使用方式

main() { bool isDartAll = languages.every((language) => language == 'Dart');}

3、源码解析

 bool every(bool test(E element)) { //利用for-in遍历集合,只要找到不符合test函数的条件,就返回false. for (E element in this) { if (!test(element)) return false; }//遍历完集合后,找到所有元素符合条件就返回true  return true; }

六、where

1、介绍

Iterable where(bool test(E element))

where函数主要用于过滤符合条件的元素,类似Kotlin中的filter的作用,最后返回符合条件元素的集合。
where函数接收一个bool test(E element)函数作为参数,最后返回一个泛型参数为E的Iterable。类似map一样,where这里也是返回一个惰性的Iterable, 然后对它的iterator进行迭代,对每个元素都执行test方法。

2、使用方式

main() { List numbers = [0, 3, 1, 2, 7, 12, 2, 4]; print(numbers.where((num) => num > 6));//输出: (7,12) //注意: 这里是print的内容实际上输出的是Iterable的toString方法返回的内容。}

3、源码解析

  • 1、首先,需要明确一点numbers实际上是一个_GrowableList和map的分析原理类似,最终还是调用了ListMixin中的where函数。
//可以看到这里是直接返回一个WhereIterable对象,而不是返回过滤后元素集合,所以它返回的Iterable也是惰性的。Iterable where(bool test(E element)) => WhereIterable(this, test);
  • 2、然后,继续深入研究下WhereIterable是如何实现的
class WhereIterable extends Iterable { final Iterable _iterable;//传入的原集合 final _ElementPredicate _f;//传入的where函数中闭包参数 WhereIterable(this._iterable, this._f); //注意: 这里WhereIterable的迭代借助了iterator,这里是直接创建一个WhereIterator,并传入元集合_iterable中的iterator以及过滤操作函数。 Iterator get iterator => new WhereIterator(_iterable.iterator, _f); // Specialization of [Iterable.map] to non-EfficientLengthIterable. Iterable map(T f(E element)) => new MappedIterable._(this, f);}
  • 3、然后,继续深入研究下WhereIterator是如何实现的
class WhereIterator extends Iterator { final Iterator _iterator;//存储集合中的iterator对象 final _ElementPredicate _f;//存储where函数传入闭包函数 WhereIterator(this._iterator, this._f); //重写moveNext函数 bool moveNext() { //遍历原集合的_iterator while (_iterator.moveNext()) { //注意: 这里会执行_f函数,如果满足条件就会返回true, 不符合条件的直接略过,迭代下一个元素; //那么外部迭代时候,就可以通过current获得当前元素,这样就实现了在原集合基础上过滤拿到符合条件的元素。 if (_f(_iterator.current)) { return true; } } //迭代完_iterator所有元素后返回false,以此来终止外部迭代。 return false; } //重写current的属性方法 E get current => _iterator.current;}
  • 4、一般在使用的WhereIterator的时候,外部肯定还有一层while迭代,但是这个WhereIterator比较特殊,moveNext()的返回值由where中闭包函数参数返回值决定的,符合条件元素moveNext()就返回true,不符合就略过,迭代检查下一个元素,直至整个集合迭代完毕,moveNext()返回false,以此也就终止了外部的迭代循环。
  • 5、上面分析,WhereIterable是惰性的,那它啥时候触发呢? 没错就是在迭代它的iterator时候才会触发,以上面例子为例
print(numbers.where((num) => num > 6));//输出: (7,12),最后会调用Iterable的toString方法返回的内容。//看下Iterable的toString方法实现String toString() => IterableBase.iterableToShortString(this, '(', ')');//这就是为啥输出样式是 (7,12)//继续查看IterableBase.iterableToShortString static String iterableToShortString(Iterable iterable, [String leftDelimiter = '(', String rightDelimiter = ')']) { if (_isToStringVisiting(iterable)) { if (leftDelimiter == "(" && rightDelimiter == ")") { // Avoid creating a new string in the "common" case. return "(...)"; } return "$leftDelimiter...$rightDelimiter"; } List parts = []; _toStringVisiting.add(iterable); try { _iterablePartsToStrings(iterable, parts);//注意:这里实际上就是通过将iterable转化成List,内部就是通过迭代iterator,以此会触发WhereIterator中的_f函数。 } finally { assert(identical(_toStringVisiting.last, iterable)); _toStringVisiting.removeLast(); } return (StringBuffer(leftDelimiter) ..writeAll(parts, ", ") ..write(rightDelimiter)) .toString(); } /// Convert elements of [iterable] to strings and store them in [parts]. 这个函数代码实现比较多,这里给出部分代码void _iterablePartsToStrings(Iterable iterable, List parts) { ... int length = 0; int count = 0; Iterator it = iterable.iterator; // Initial run of elements, at least headCount, and then continue until // passing at most lengthLimit characters. //可以看到这是外部迭代while while (length < lengthLimit || count < headCount) { if (!it.moveNext()) return;//这里实际上调用了WhereIterator中的moveNext函数,经过_f函数处理的moveNext() String next = "${it.current}";//获取current. parts.add(next); length += next.length + overhead; count++; } ...}

七、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()})

首先从源码声明结构上来看,firstWhere、lastWhere和singleWhere是一样,它们都是接收两个参数,一个是必需参数:test筛选条件闭包函数,另一个是可选参数:orElse闭包函数。

但是它们用法却不同,firstWhere主要是用于筛选顺序第一个符合条件的元素,可能存在多个符合条件元素;lastWhere主要是用于筛选顺序最后一个符合条件的元素,可能存在多个符合条件元素;singleWhere主要是用于筛选顺序唯一一个符合条件的元素,不可能存在多个符合条件元素,存在的话就会抛出异常IterableElementError.tooMany(), 所以使用它的使用需要谨慎注意下

2、使用方式

main() { var numbers = [0, 3, 1, 2, 7, 12, 2, 4]; //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值-1 print(numbers.firstWhere((num) => num == 5, orElse: () => -1));  //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值-1 print(numbers.lastWhere((num) => num == 2, orElse: () => -1));  //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值,前提是集合中只有一个符合条件的元素, 否则就会抛出异常 print(numbers.singleWhere((num) => num == 4, orElse: () => -1)); }

3、源码解析

 //firstWhere E firstWhere(bool test(E element), {E orElse()}) { for (E element in this) {//直接遍历原集合,只要找到第一个符合条件的元素就直接返回,终止函数 if (test(element)) return element; } if (orElse != null) return orElse();//遍历完集合后,都没找到符合条件的元素并且外部传入了orElse就会触发orElse函数 //否则找不到元素,直接抛出异常。所以这里需要注意下,如果不想抛出异常,可能你需要处理下orElse函数。 throw IterableElementError.noElement(); } //lastWhere E lastWhere(bool test(E element), {E orElse()}) { E result;//定义result来记录每次符合条件的元素 bool foundMatching = false;//定义一个标志位是否找到符合匹配的。 for (E element in this) { if (test(element)) {//每次找到符合条件的元素,都会重置result,所以result记录了最新的符合条件元素,那么遍历到最后,它也就是最后一个符合条件的元素 result = element; foundMatching = true;//找到后重置标记位 } } if (foundMatching) return result;//如果标记位为true直接返回result即可 if (orElse != null) return orElse();//处理orElse函数 //同样,找不到元素,直接抛出异常。可能你需要处理下orElse函数。 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,说明已经找到一个符合条件的元素,如果触发这条逻辑分支,说明不止一个元素符合条件就直接抛出IterableElementError.tooMany()异常 throw IterableElementError.tooMany(); } result = element; foundMatching = true; } } if (foundMatching) return result; if (orElse != null) return orElse(); //同样,找不到元素,直接抛出异常。可能你需要处理下orElse函数。 throw IterableElementError.noElement(); }

八、join

1、介绍

 String join([String separator = ""])

join函数主要是用于将集合所有元素值转化成字符串,中间用指定的separator连接符连接
可以看到join函数比较简单,接收一个separator分隔符的可选参数,可选参数默认值是空字符串,最后返回一个字符串。

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可选参数,默认值为"" String join([String separator = ""]) { Iterator iterator = this.iterator; if (!iterator.moveNext()) return ""; //创建StringBuffer StringBuffer buffer = StringBuffer(); //如果分隔符为空或空字符串 if (separator == null || separator == "") { //do-while遍历iterator,然后直接拼接元素 do { buffer.write("${iterator.current}"); } while (iterator.moveNext()); } else { //如果分隔符不为空 //先加入第一个元素 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函数主要是用于截取原集合前count个元素组成的集合,take函数接收一个count作为函数参数,最后返回一个泛型参数为E的Iterable。类似where一样,take这里也是返回一个惰性的Iterable, 然后对它的iterator进行迭代。

takeWhile函数主要用于

2、使用方式

main() { List numbers = [0, 3, 1, 2, 7, 12, 2, 4]; print(numbers.take(5));//输出(0, 3, 1, 2, 7)}

3、源码解析

  • 1、首先, 需要明确一点numbers.take调用了ListMixin中的take函数,可以看到并没有直接返回集合前count个元素,而是返回一个TakeIterable惰性Iterable。
 Iterable take(int count) { return TakeIterable(this, count); }
  • 2、然后,继续深入研究TakeIterable
class TakeIterable extends Iterable { final Iterable _iterable;//存储原集合 final int _takeCount;//take count factory TakeIterable(Iterable iterable, int takeCount) { ArgumentError.checkNotNull(takeCount, "takeCount"); RangeError.checkNotNegative(takeCount, "takeCount"); if (iterable is EfficientLengthIterable) {//如果原集合是EfficientLengthIterable,就返回创建EfficientLengthTakeIterable return new EfficientLengthTakeIterable(iterable, takeCount); } //否则就返回TakeIterable return new TakeIterable._(iterable, takeCount); } TakeIterable._(this._iterable, this._takeCount);//注意: 这里是返回了TakeIterator,并传入原集合的iterator以及_takeCount Iterator get iterator { return new TakeIterator(_iterable.iterator, _takeCount); }}
  • 3、然后,继续深入研究TakeIterator.
class TakeIterator extends Iterator { final Iterator _iterator;//存储原集合中的iterator int _remaining;//存储需要截取的前几个元素的数量 TakeIterator(this._iterator, this._remaining) { assert(_remaining >= 0); } bool moveNext() { _remaining--;//通过_remaining作为游标控制迭代次数 if (_remaining >= 0) {//如果_remaining大于等于0就会继续执行moveNext方法 return _iterator.moveNext(); } _remaining = -1; return false;//如果_remaining小于0就返回false,终止外部循环 } E get current { if (_remaining < 0) return null; return _iterator.current; }}
  • 4、所以上述例子中最终还是调用Iterable的toString方法,方法中会进行iterator的迭代,最终会触发惰性TakeIterable中的TakeIterator的moveNext方法。

十、takeWhile

1、介绍

 Iterable takeWhile(bool test(E value))

takeWhile函数主要用于依次选择满足条件的元素,直到遇到第一个不满足的元素,并停止选择。takeWhile函数接收一个test条件函数作为函数参数,然后返回一个惰性的Iterable。

2、使用方式

main() { List numbers = [3, 1, 2, 7, 12, 2, 4]; print(numbers.takeWhile((number) => number > 2).toList());//输出: [3] 遇到1的时候就不满足大于2条件就终止筛选。}

3、源码解析

  • 1、首先,因为numbers是List所以还是调用ListMixin中的takeWhile函数
 Iterable takeWhile(bool test(E element)) { return TakeWhileIterable(this, test);//可以看到它仅仅返回的是TakeWhileIterable,而不是筛选后符合条件的集合,所以它是惰性。 }
  • 2、然后,继续看下TakeWhileIterable的实现
class TakeWhileIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; TakeWhileIterable(this._iterable, this._f); Iterator get iterator { //重写iterator,创建一个TakeWhileIterator对象并返回。 return new TakeWhileIterator(_iterable.iterator, _f); }}//TakeWhileIteratorclass TakeWhileIterator extends Iterator { final Iterator _iterator; final _ElementPredicate _f; bool _isFinished = false; TakeWhileIterator(this._iterator, this._f); bool moveNext() { if (_isFinished) return false; //原集合_iterator遍历结束或者原集合中的当前元素current不满足_f条件,就返回false以此终止外部的迭代。 //进一步说明了只有moveNext调用,才会触发_f的执行,此时惰性的Iterable才得以执行。 if (!_iterator.moveNext() || !_f(_iterator.current)) { _isFinished = true;//迭代结束重置_isFinished为true return false; } return true; } E get current { if (_isFinished) return null;//如果迭代结束,还取current就直接返回null了 return _iterator.current; }}

十、skip

1、介绍

 Iterable skip(int count)

skip函数主要是用于跳过原集合前count个元素后,剩下元素组成的集合,skip函数接收一个count作为函数参数,最后返回一个泛型参数为E的Iterable。类似where一样,skip这里也是返回一个惰性的Iterable, 然后对它的iterator进行迭代。

2、使用方式

main() { List numbers = [3, 1, 2, 7, 12, 2, 4]; print(numbers.skip(2).toList());//输出: [2, 7, 12, 2, 4] 跳过前两个元素3,1 直接从第3个元素开始 }

3、源码解析

  • 1、首先,因为numbers是List所以还是调用ListMixin中的skip函数
Iterable skip(int count) => SubListIterable(this, count, null);//返回的是一个SubListIterable惰性Iterable,传入原集合和需要跳过的count大小
  • 2、然后,继续看下SubListIterable的实现,这里只看下elementAt函数实现
class SubListIterable extends ListIterable { final Iterable _iterable; // Has efficient length and elementAt. final int _start;//这是传入的需要skip的count final int _endOrLength;//这里传入为null ... int get _endIndex { int length = _iterable.length;//获取原集合长度 if (_endOrLength == null || _endOrLength > length) return length;//_endIndex为原集合长度 return _endOrLength; } int get _startIndex {//主要看下_startIndex的实现 int length = _iterable.length;//获取原集合长度 if (_start > length) return length;//如果skip的count超过集合自身长度,_startIndex为原集合长度 return _start;//否则返回skip的count } E elementAt(int index) { int realIndex = _startIndex + index;//相当于把原集合中每个元素原来index,整体向后推了_startIndex,最后获取真实映射的realIndex if (index < 0 || realIndex >= _endIndex) {//如果realIndex越界就会抛出异常 throw new RangeError.index(index, this, "index"); } return _iterable.elementAt(realIndex);//否则就取对应realIndex在原集合中的元素。 } ...}

十一、skipWhile

1、介绍

 Iterable skipWhile(bool test(E element))

skipWhile函数主要用于依次跳过满足条件的元素,直到遇到第一个不满足的元素,并停止筛选。skipWhile函数接收一个test条件函数作为函数参数,然后返回一个惰性的Iterable。

2、使用方式

main() { List numbers = [3, 1, 2, 7, 12, 2, 4]; print(numbers.skipWhile((number) => number < 4).toList());//输出: [7, 12, 2, 4] //因为3、1、2都是满足小于4的条件,所以直接skip跳过,直到遇到7不符合条件停止筛选,剩下的就是[7, 12, 2, 4]}

3、源码解析

  • 1、首先,因为numbers是List所以还是调用ListMixin中的skipWhile函数
 Iterable skipWhile(bool test(E element)) { return SkipWhileIterable(this, test);//可以看到它仅仅返回的是SkipWhileIterable,而不是筛选后符合条件的集合,所以它是惰性的。 }
  • 2、然后,继续看下SkipWhileIterable的实现
class SkipWhileIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; SkipWhileIterable(this._iterable, this._f); //重写iterator,创建一个SkipWhileIterator对象并返回。 Iterator get iterator { return new SkipWhileIterator(_iterable.iterator, _f); }}//SkipWhileIteratorclass SkipWhileIterator extends Iterator { final Iterator _iterator;//存储原集合的iterator final _ElementPredicate _f;//存储skipWhile中筛选闭包函数 bool _hasSkipped = false;//判断是否已经跳过元素的标识,默认为false SkipWhileIterator(this._iterator, this._f);//重写moveNext函数 bool moveNext() { if (!_hasSkipped) {//如果是最开始第一次没有跳过任何元素 _hasSkipped = true;//然后重置标识为true,表示已经进行了第一次跳过元素的操作 while (_iterator.moveNext()) {//迭代原集合中的iterator if (!_f(_iterator.current)) return true;//只要找到符合条件的元素,就略过迭代下一个元素,不符合条件就直接返回true终止当前moveNext函数,而此时外部迭代循环正式从当前元素开始迭代, } } return _iterator.moveNext();//那么遇到第一个不符合条件元素之后所有元素就会通过_iterator.moveNext()正常返回 } E get current => _iterator.current;}

十二、follwedBy

1、介绍

Iterable followedBy(Iterable other)

followedBy函数主要用于在原集合后面追加拼接另一个Iterable集合,followedBy函数接收一个Iterable参数,最后又返回一个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、首先,还是调用ListMixin中的followedBy函数
 Iterable followedBy(Iterable other) => FollowedByIterable.firstEfficient(this, other);//这里实际上还是返回一个惰性的FollowedByIterable对象,这里使用命名构造器firstEfficient创建对象
  • 2、然后,继续看下FollowedByIterable中的firstEfficient实现
 factory FollowedByIterable.firstEfficient( EfficientLengthIterable first, Iterable second) { if (second is EfficientLengthIterable) {//List肯定是一个EfficientLengthIterable,所以会创建一个EfficientLengthFollowedByIterable,传入的参数first是当前集合,second是需要在后面拼接的集合 return new EfficientLengthFollowedByIterable(first, second); } return new FollowedByIterable(first, second); }
  • 3、然后,继续看下EfficientLengthFollowedByIterable的实现,这里只具体看下elementAt函数的实现
class EfficientLengthFollowedByIterable extends FollowedByIterable implements EfficientLengthIterable { EfficientLengthFollowedByIterable( EfficientLengthIterable first, EfficientLengthIterable second) : super(first, second); ...  E elementAt(int index) {//elementAt在迭代过程会调用 int firstLength = _first.length;//取原集合的长度 if (index < firstLength) return _first.elementAt(index);//如果index小于原集合长度就从原集合中获取元素 return _second.elementAt(index - firstLength);//否则就通过index - firstLength 计算新的下标从拼接的集合中获取元素。 } ...}

十三、expand

1、介绍

Iterable expand(Iterable f(E element)) 

expand函数主要用于将集合中每个元素扩展为零个或多个元素或者将多个元素组成二维数组展开成平铺一个一维数组。expand函数接收一个Iterable f(E element)函数作为函数参数。这个闭包函数比较特别,特别之处在于f函数返回的是一个Iterable,那么就意味着可以将原集合中每个元素扩展成多个相同元素。注意expand函数最终还是返回一个惰性的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、首先还是调用ListMixin中的expand函数。
 Iterable expand(Iterable f(E element)) => ExpandIterable(this, f);//可以看到这里并没有直接返回扩展的集合,而是创建一个惰性的ExpandIterable对象返回,
  • 2、然后继续深入ExpandIterable
typedef Iterable _ExpandFunction(S sourceElement);class ExpandIterable extends Iterable { final Iterable _iterable; final _ExpandFunction _f; ExpandIterable(this._iterable, this._f); Iterator get iterator => new ExpandIterator(_iterable.iterator, _f);//注意: 这里iterator是一个ExpandIterator对象,传入的是原集合的iterator和expand函数中闭包函数参数_f}//ExpandIterator的实现class ExpandIterator implements Iterator { final Iterator _iterator; final _ExpandFunction _f; //创建一个空的Iterator对象_currentExpansion Iterator _currentExpansion = const EmptyIterator(); T _current; ExpandIterator(this._iterator, this._f); T get current => _current;//重写current//重写moveNext函数,只要当迭代的时候,moveNext执行才会触发闭包函数_f执行。 bool moveNext() { //如果_currentExpansion返回false终止外部迭代循环 if (_currentExpansion == null) return false; //开始_currentExpansion是一个空的Iterator对象,所以moveNext()为false while (!_currentExpansion.moveNext()) { _current = null; //迭代原集合中的_iterator if (_iterator.moveNext()) { //如果_f抛出异常,先重置_currentExpansion为null, 遇到 if (_currentExpansion == null) return false;就会终止外部迭代 _currentExpansion = null; _currentExpansion = _f(_iterator.current).iterator;//执行_f函数 } else { return false; } } _current = _currentExpansion.current; return true; }}

十四、reduce

1、介绍

E reduce(E combine(E previousValue, E element))T fold(T initialValue, T combine(T previousValue, E element))

reduce函数主要用于集合中元素依次归纳(combine),每次归纳后的结果会和下一个元素进行归纳,它可以用来累加或累乘,具体取决于combine函数中操作,combine函数中会回调上一次归纳后的值和当前元素值,reduce提供的是获取累积迭代结果的便利条件. fold和reduce几乎相同,唯一区别是fold可以指定初始值。 但是需要注意的是,combine函数返回值的类型必须和集合泛型类型一致。

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); //求平均数 print(numbers.fold(2, (prev, curr) => (prev as int) + curr) / numbers.length); //求平均数 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];//初始值默认取第一个 for (int i = 1; i < length; i++) {//从第二个开始遍历 value = combine(value, this[i]);//combine回调value值和当前元素值,然后把combine的结果归纳到value上,依次处理。 if (length != this.length) { throw ConcurrentModificationError(this);//注意: 在操作过程中不允许删除和添加元素否则就会出现ConcurrentModificationError } } return value; } T fold(T initialValue, T combine(T previousValue, E element)) { var value = initialValue;//和reduce唯一区别在于这里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函数用于获取对应index下标的元素,传入一个index参数,返回对应泛型类型E的元素。

2、使用方式

main() { print(numbers.elementAt(3));//elementAt一般不会直接使用,更多是使用[],运算符重载的方式间接使用。 }

3、源码解析

 E elementAt(int index) { ArgumentError.checkNotNull(index, "index"); RangeError.checkNotNegative(index, "index"); int elementIndex = 0; //for-in遍历原集合,找到对应elementIndex元素并返回 for (E element in this) { if (index == elementIndex) return element; elementIndex++; } //找不到抛出RangeError throw RangeError.index(index, this, "index", null, elementIndex); }

总结

到这里,有关dart中集合操作符函数相关内容就结束了,关于集合操作符函数使用在Flutter中开发非常有帮助,特别在处理集合数据中,可以让你的代码实现更优雅,不要再是一上来就for循环直接开干,虽然也能实现,但是如果能适当使用操作符函数,将会使代码更加简洁。欢迎继续关注,下一篇Dart中的函数的使用…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在Java中确定文件类型

以编程方式确定文件的类型可能非常棘手&#xff0c;并且已经提出并实现了许多基于内容的文件标识方法。 Java中有几种可用于检测文件类型的实现&#xff0c;其中大多数很大程度上或完全基于文件的扩展名。 这篇文章介绍了Java中最常见的文件类型检测实现。 本文介绍了几种在Ja…

程序员编程艺术第十一章:最长公共子序列(LCS)问题

程序员编程艺术第十一章&#xff1a;最长公共子序列(LCS)问题 0、前言 程序员编程艺术系列重新开始创作了&#xff08;前十章&#xff0c;请参考程序员编程艺术第一~十章集锦与总结&#xff09;。回顾之前的前十章&#xff0c;有些代码是值得商榷的&#xff0c;因当时的代码只顾…

gateway 过滤器执行顺序_Gateway网关源码解析—路由(1.1)之RouteDefinitionLocator一览...

一、概述本文主要对 路由定义定位器 RouteDefinitionLocator 做整体的认识。在 《Spring-Cloud-Gateway 源码解析 —— 网关初始化》 中&#xff0c;我们看到路由相关的组件 RouteDefinitionLocator / RouteLocator 的初始化。涉及到的类比较多&#xff0c;我们用下图重新梳理下…

ERP开发中应用字符串解析实现界面翻译智能化

ERP中要实现界面多语言的功能&#xff0c;则要对各种情况的字符串进行处理并作出翻译。有些字符串的翻译是有规律可行的&#xff0c;遵循相应的模板模式&#xff0c;解析字符串&#xff0c;可以实现机器翻译的效果。 请看帐套数据库表的设计ADCOMP CREATE TABLE dbo.ADCOMP(REC…

参数详解 复制进程_如何优化PostgreSQL逻辑复制

How to Optimize PostgreSQL Logical Replication逻辑复制( Logical Replication )或 Pglogical 是表级别的复制。两者都是基于 WAL 的复制机制&#xff0c;允许在两个实例之间复制指定表的WAL 。这两个看起来让人迷惑&#xff0c;到底有什么区别呢&#xff1f; Logical Replic…

Android Studio使用说明

声明: 本博客文章原创类别的均为个人原创&#xff0c;版权所有。转载请注明出处: http://blog.csdn.net/ml3947,另外本人的个人博客:http://www.wjfxgame.com。 凌晨的Google I/O大会上&#xff0c;宣布了Android Studio&#xff0c;引起了现场开发者的一片欢呼。那么&#x…

有些窗口底部被任务栏挡住了_开始使用 Tint2 吧,一款 Linux 中的开源任务栏

Tint2 是我们在开源工具系列中的第 14 个工具&#xff0c;它将在 2019 年提高你的工作效率&#xff0c;能在任何窗口管理器中提供一致的用户体验。-- Kevin Sonney每年年初似乎都有疯狂的冲动想提高工作效率。新年的决心&#xff0c;渴望开启新的一年&#xff0c;当然&#xff…

从jHiccup开始

写完“如何在生产中检测和诊断慢速代码”一文后&#xff0c;我受到读者的鼓励&#xff0c;尝试从Azul系统尝试jHiccup 。 去年&#xff0c;我参加了jHiccup的创建者Gil Tene的演讲&#xff0c;探讨了测量延迟的正确方法&#xff0c;其中&#xff0c;他向我们介绍了jHiccup。 它…

华为内部面试题库---(6)

1.在SMP体系结构中&#xff0c;中断亲和性是指将一个或者多个中断绑定到特定CPU core上运行&#xff0c;下列说法错误的是&#xff1a;A.每个硬件设备都会在/proc/irq下有个中断号命令的目录来标志中断亲和性B.IRQ#目录下smp_affinity文件&#xff0c;通过设置CPU位掩码&#x…

基元需要走吗?

我目前正在使用JSF作为视图技术&#xff0c;使用JPA作为持久层的企业应用程序。 它可能是支持bean或服务方法中的某种东西&#xff0c;但令我震惊&#xff1a;是否有充分的理由在企业应用程序中使用原语&#xff1f; 当我开始围绕J2SE 1.2使用Java进行编程&#xff08;或者是J…

输入参数_太实用!输入参数1秒算出功率,这款计算工具又快又准

随着互联网红利的不断加深&#xff0c;到了后期&#xff0c;不断地各种工具开始涌现&#xff0c;方便了很多用户&#xff0c;填补了市场上的很多空白&#xff0c;有生活娱乐类、提高效率类、垂直专业类、系统工具类等等。工业行业作为各行各业的大头&#xff0c;机械化、智能化…

如何编写NetBeans插件

是否想在NetBeans IDE中添加功能或自动执行某些操作&#xff1f; 跟随我们编写您的第一个NetBeans插件。 让我们超越简单的工具栏示例 &#xff0c;创建一个可以自动更新的插件。 该代码基于NetBeans的WakaTime插件 。 我们的示例插件将仅打印Hello World语句&#xff0c;并在…

Spring Batch教程–最终指南

这是Spring批处理教程&#xff0c;它是Spring框架的一部分。 Spring Batch提供了可重用的功能&#xff0c;这些功能对于处理大量记录至关重要&#xff0c;包括日志记录/跟踪&#xff0c;事务管理&#xff0c;作业处理统计信息&#xff0c;作业重新启动&#xff0c;跳过和资源管…

箱式图 添加异常值平均值_什么是脏数据?怎样用箱形图分析异常值?终于有人讲明白了...

导读&#xff1a;数据质量分析是数据挖掘中数据准备过程的重要一环&#xff0c;是数据预处理的前提&#xff0c;也是数据挖掘分析结论有效性和准确性的基础。没有可信的数据&#xff0c;数据挖掘构建的模型将是空中楼阁。数据质量分析的主要任务是检查原始数据中是否存在脏数据…

窗口程序ImageView(仿QQ图片查看器)

近期一直在学习窗口程序之类的问题,下午正好有机会和大家讨论一下. 程序运行截图&#xff1a; 应用方法&#xff1a; 1、直接把图像文件拖到图标上表现 2、通过命令行方式&#xff0c;示例&#xff1a;ImageView.exe "带全路径的图像文件名称" 3、打开ImageView.exe&…

USACO3.15stamps(dp)

对dp很无奈。。枚举所有可能达到的值 dp[i]表示到达i值所用最少的邮票 1 /*2 ID: shangca23 LANG: C4 TASK: stamps5 */6 #include <iostream>7 #include<cstdio>8 #include<cstring>9 #include<stdlib.h> 10 #include<algorithm> …

基于价值的类

在Java 8中&#xff0c;某些类在Javadoc中有一个小注释&#xff0c;说明它们是基于值的类 。 其中包括简短说明的链接&#xff0c;以及有关不使用它们的限制。 这很容易被忽略&#xff0c;如果这样做&#xff0c;则可能会在将来的Java版本中以微妙的方式破坏代码。 为了避免这种…

REST API的演变

每个开发人员都以某种方式接触到API 。 要么为一家大公司集成一个主要系统&#xff0c;或者使用最新的图形库生成一些精美的图表&#xff0c;要么直接与他喜欢的编程语言进行交互。 事实是&#xff0c;API无处不在&#xff01; 它们实际上代表了当今Internet的基本构建块&#…

Oracle MAF中的LOV

我们都喜欢最强大的ADF功能值列表之一。 使用它们&#xff0c;我们可以在ADF应用程序中声明性地轻松构建非常复杂的功能。 一件好事是&#xff0c;我们在Oracle MAF中也有类似的方法。 在ADF BC中&#xff0c;我们在业务服务级别&#xff08;基本上在实体或VO级别&#xff09;定…

怎么移动矩形选框工具选中的东西_ps矩形选框工具怎么用,你值得一看的技巧...

PS是一款非常好用的图片制作软件&#xff0c;我们可以使用矩形选框工具&#xff0c;选择自己需要的区域进行操作&#xff0c;下面小编就教大家ps矩形选框工具怎么用&#xff0c;希望可以帮助到大家。操作方法01首先我们打开PS进入到主界面&#xff0c;如图所示。02之后我们需要…