简述:

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

一、Iterable<E>

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

1、Iterable类关系图

2、Iterable类方法图

二、forEach

1、介绍

void forEach(void f(E element))

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

2、使用方式

main() {var languages = <String>['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<T> map<T>(T f(E e))

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

2、使用方式

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

3、源码解析

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

@pragma("vm:entry-point")
class _GrowableList<T> extends ListBase<T> {//_GrowableList<T>是继承了ListBase<T>...
}abstract class ListBase<E> extends Object with ListMixin<E> {//ListBase mixin with ListMixin<E>...
}

  • 2、然后可以看到ListMixin<E>实际上实现了List<E>,然后List<E>继承了EfficientLengthIterable<E>,最后EfficientLengthIterable<E>继承Iterable<E>,所以最终的map函数来自于Iterable<E>但是具体的实现定义在ListMinxin<E>中。
abstract class ListMixin<E> implements List<E> {...//可以看到这里是直接返回一个MappedListIterable,它是一个惰性IterableIterable<T> map<T>(T f(E element)) => MappedListIterable<E, T>(this, f);...
}

  • 3、为什么是惰性的呢,可以看到它并不是直接返回转化后的集合,而是返回一个带有值的MappedListIterable的,如果不执行elementAt方法,是不会触发执行map传入的f函数, 所以它是惰性的。
class MappedListIterable<S, T> extends ListIterable<T> {final Iterable<S> _source;//_source存储了所携带的原集合final _Transformation<S, T> _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<E> toList({bool growable = true}) {List<E> result;if (growable) {result = <E>[]..length = length;} else {result = List<E>(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函数的条件,就返回truefor (int i = 0; i < length; i++) {if (test(this[i])) return true;if (length != this.length) {throw ConcurrentModificationError(this);}}//遍历完集合后,未找到符合条件的集合就返回falsereturn 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<E> where(bool test(E element))

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

2、使用方式

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

3、源码解析

  • 1、首先,需要明确一点numbers实际上是一个_GrowableList<T>map的分析原理类似,最终还是调用了ListMixin中的where函数。
//可以看到这里是直接返回一个WhereIterable对象,而不是返回过滤后元素集合,所以它返回的Iterable也是惰性的。
Iterable<E> where(bool test(E element)) => WhereIterable<E>(this, test);

  • 2、然后,继续深入研究下WhereIterable是如何实现的
class WhereIterable<E> extends Iterable<E> {final Iterable<E> _iterable;//传入的原集合final _ElementPredicate<E> _f;//传入的where函数中闭包参数WhereIterable(this._iterable, this._f);//注意: 这里WhereIterable的迭代借助了iterator,这里是直接创建一个WhereIterator,并传入元集合_iterable中的iterator以及过滤操作函数。Iterator<E> get iterator => new WhereIterator<E>(_iterable.iterator, _f);// Specialization of [Iterable.map] to non-EfficientLengthIterable.Iterable<T> map<T>(T f(E element)) => new MappedIterable<E, T>._(this, f);
}

  • 3、然后,继续深入研究下WhereIterator是如何实现的
class WhereIterator<E> extends Iterator<E> {final Iterator<E> _iterator;//存储集合中的iterator对象final _ElementPredicate<E> _f;//存储where函数传入闭包函数WhereIterator(this._iterator, this._f);//重写moveNext函数bool moveNext() {//遍历原集合的_iteratorwhile (_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.iterableToShortStringstatic 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<String> parts = <String>[];_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<String> 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.//可以看到这是外部迭代whilewhile (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 = <int>[0, 3, 1, 2, 7, 12, 2, 4];//注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值-1print(numbers.firstWhere((num) => num == 5, orElse: () => -1)); //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值-1print(numbers.lastWhere((num) => num == 2, orElse: () => -1)); //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值,前提是集合中只有一个符合条件的元素, 否则就会抛出异常print(numbers.singleWhere((num) => num == 4, orElse: () => -1));
}

3、源码解析

//firstWhereE 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();}//lastWhereE 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();}//singleWhereE 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 = <int>[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<E> iterator = this.iterator;if (!iterator.moveNext()) return "";//创建StringBufferStringBuffer buffer = StringBuffer();//如果分隔符为空或空字符串if (separator == null || separator == "") {//do-while遍历iterator,然后直接拼接元素do {buffer.write("${iterator.current}");} while (iterator.moveNext());} else {//如果分隔符不为空//先加入第一个元素buffer.write("${iterator.current}");//然后while遍历iteratorwhile (iterator.moveNext()) {buffer.write(separator);//先拼接分隔符buffer.write("${iterator.current}");//再拼接元素}}return buffer.toString();//最后返回最终的字符串。}

九、take

1、介绍

Iterable<E> take(int count)

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

takeWhile函数主要用于

2、使用方式

main() {List<int> 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<E>惰性Iterable
Iterable<E> take(int count) {return TakeIterable<E>(this, count);}

  • 2、然后,继续深入研究TakeIterable
class TakeIterable<E> extends Iterable<E> {final Iterable<E> _iterable;//存储原集合final int _takeCount;//take countfactory TakeIterable(Iterable<E> iterable, int takeCount) {ArgumentError.checkNotNull(takeCount, "takeCount");RangeError.checkNotNegative(takeCount, "takeCount");if (iterable is EfficientLengthIterable) {//如果原集合是EfficientLengthIterable,就返回创建EfficientLengthTakeIterablereturn new EfficientLengthTakeIterable<E>(iterable, takeCount);}//否则就返回TakeIterablereturn new TakeIterable<E>._(iterable, takeCount);}TakeIterable._(this._iterable, this._takeCount);//注意: 这里是返回了TakeIterator,并传入原集合的iterator以及_takeCountIterator<E> get iterator {return new TakeIterator<E>(_iterable.iterator, _takeCount);}
}

  • 3、然后,继续深入研究TakeIterator.
class TakeIterator<E> extends Iterator<E> {final Iterator<E> _iterator;//存储原集合中的iteratorint _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、所以上述例子中最终还是调用IterabletoString方法,方法中会进行iterator的迭代,最终会触发惰性TakeIterable中的TakeIteratormoveNext方法。

十、takeWhile

1、介绍

Iterable<E> takeWhile(bool test(E value))

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

2、使用方式

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

3、源码解析

  • 1、首先,因为numbersList<int>所以还是调用ListMixin中的takeWhile函数
Iterable<E> takeWhile(bool test(E element)) {return TakeWhileIterable<E>(this, test);//可以看到它仅仅返回的是TakeWhileIterable,而不是筛选后符合条件的集合,所以它是惰性。}

  • 2、然后,继续看下TakeWhileIterable<E>的实现
class TakeWhileIterable<E> extends Iterable<E> {final Iterable<E> _iterable;final _ElementPredicate<E> _f;TakeWhileIterable(this._iterable, this._f);Iterator<E> get iterator {//重写iterator,创建一个TakeWhileIterator对象并返回。return new TakeWhileIterator<E>(_iterable.iterator, _f);}
}//TakeWhileIterator
class TakeWhileIterator<E> extends Iterator<E> {final Iterator<E> _iterator;final _ElementPredicate<E> _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为truereturn false;}return true;}E get current {if (_isFinished) return null;//如果迭代结束,还取current就直接返回null了return _iterator.current;}
}

十、skip

1、介绍

Iterable<E> skip(int count)

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

2、使用方式

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

3、源码解析

  • 1、首先,因为numbersList<int>所以还是调用ListMixin中的skip函数
Iterable<E> skip(int count) => SubListIterable<E>(this, count, null);//返回的是一个SubListIterable惰性Iterable,传入原集合和需要跳过的count大小

  • 2、然后,继续看下SubListIterable<E>的实现,这里只看下elementAt函数实现
class SubListIterable<E> extends ListIterable<E> {final Iterable<E> _iterable; // Has efficient length and elementAt.final int _start;//这是传入的需要skip的countfinal 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,最后获取真实映射的realIndexif (index < 0 || realIndex >= _endIndex) {//如果realIndex越界就会抛出异常throw new RangeError.index(index, this, "index");}return _iterable.elementAt(realIndex);//否则就取对应realIndex在原集合中的元素。}...
}

十一、skipWhile

1、介绍

Iterable<E> skipWhile(bool test(E element))

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

2、使用方式

main() {List<int> 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、首先,因为numbersList<int>所以还是调用ListMixin中的skipWhile函数
Iterable<E> skipWhile(bool test(E element)) {return SkipWhileIterable<E>(this, test);//可以看到它仅仅返回的是SkipWhileIterable,而不是筛选后符合条件的集合,所以它是惰性的。}

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

十二、follwedBy

1、介绍

Iterable<E> followedBy(Iterable<E> other)

followedBy函数主要用于在原集合后面追加拼接另一个Iterable<E>集合,followedBy函数接收一个Iterable<E>参数,最后又返回一个Iterable<E>类型的值。

2、使用方式

main() {var languages = <String>['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<E> followedBy(Iterable<E> other) =>FollowedByIterable<E>.firstEfficient(this, other);//这里实际上还是返回一个惰性的FollowedByIterable对象,这里使用命名构造器firstEfficient创建对象

  • 2、然后,继续看下FollowedByIterable中的firstEfficient实现
factory FollowedByIterable.firstEfficient(EfficientLengthIterable<E> first, Iterable<E> second) {if (second is EfficientLengthIterable<E>) {//List肯定是一个EfficientLengthIterable,所以会创建一个EfficientLengthFollowedByIterable,传入的参数first是当前集合,second是需要在后面拼接的集合return new EfficientLengthFollowedByIterable<E>(first, second);}return new FollowedByIterable<E>(first, second);}

  • 3、然后,继续看下EfficientLengthFollowedByIterable的实现,这里只具体看下elementAt函数的实现
class EfficientLengthFollowedByIterable<E> extends FollowedByIterable<E>implements EfficientLengthIterable<E> {EfficientLengthFollowedByIterable(EfficientLengthIterable<E> first, EfficientLengthIterable<E> 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<T> expand<T>(Iterable<T> f(E element))

expand函数主要用于将集合中每个元素扩展为零个或多个元素或者将多个元素组成二维数组展开成平铺一个一维数组。 expand函数接收一个Iterable<T> f(E element)函数作为函数参数。这个闭包函数比较特别,特别之处在于f函数返回的是一个Iterable<T>,那么就意味着可以将原集合中每个元素扩展成多个相同元素。注意expand函数最终还是返回一个惰性的Iterable<T>

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<T> expand<T>(Iterable<T> f(E element)) =>ExpandIterable<E, T>(this, f);//可以看到这里并没有直接返回扩展的集合,而是创建一个惰性的ExpandIterable对象返回,

  • 2、然后继续深入ExpandIterable
typedef Iterable<T> _ExpandFunction<S, T>(S sourceElement);class ExpandIterable<S, T> extends Iterable<T> {final Iterable<S> _iterable;final _ExpandFunction<S, T> _f;ExpandIterable(this._iterable, this._f);Iterator<T> get iterator => new ExpandIterator<S, T>(_iterable.iterator, _f);//注意: 这里iterator是一个ExpandIterator对象,传入的是原集合的iterator和expand函数中闭包函数参数_f
}//ExpandIterator的实现
class ExpandIterator<S, T> implements Iterator<T> {final Iterator<S> _iterator;final _ExpandFunction<S, T> _f;//创建一个空的Iterator对象_currentExpansionIterator<T> _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()为falsewhile (!_currentExpansion.moveNext()) {_current = null;//迭代原集合中的_iteratorif (_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>(T initialValue, T combine(T previousValue, E element))

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

2、使用方式

main() {List<int> 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 = <String>['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>(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++;}//找不到抛出RangeErrorthrow RangeError.index(index, this, "index", null, elementIndex);}

总结

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

dart string 转 bool_Dart语法篇之集合操作符函数与源码分析(三)相关推荐

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

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

  2. 【投屏】Scrcpy源码分析三(Client篇-投屏阶段)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  3. bytebuf池_Netty篇:ByteBuf之内存池源码分析

    Netty的内存池的实现有些复杂,使用了大量的位运算,晦涩难懂,不过万能的博客上好多大神已经介绍的非常详细,推荐四篇很详细很棒的源码分析的文章链接,本文根据自己的理解顺一下思路,内容主要也是出自以下四 ...

  4. spring boot实战(第六篇)加载application资源文件源码分析

    前言 在上一篇中了解了spring配置资源的加载过程,本篇在此基础上学习spring boot如何默认加载application.xml等文件信息的. ConfigFileApplicationLis ...

  5. k8s驱逐篇(3)-kubelet节点压力驱逐-源码分析篇

    kubelet节点压力驱逐-概述 kubelet监控集群节点的 CPU.内存.磁盘空间和文件系统的inode 等资源,根据kubelet启动参数中的驱逐策略配置,当这些资源中的一个或者多个达到特定的消 ...

  6. java.lang.object源码_第三篇:java.lang.Object 类源码分析

    Object所包含的方法如下: ① public Object(); 构造函数: 大部分情况下,类对象的声明,都是通过构造函数完成的(Java中规定:在类定义过程中,对于未定义构造函数的类,默认会有一 ...

  7. 集合框架源码分析三(实现类篇ArrayList,LinkedList,HashMap)

    一.ArrayList,可自动扩充容量的动态数组 public class ArrayList<E> extends AbstractList<E> implements Li ...

  8. spring boot实战(第十篇)Spring boot Bean加载源码分析

    前言 前面的文章描述了Application对应Bean的创建,本篇将阐述spring boot中bean的创建过程 refresh 首先来看SpringApplication#run方法中refre ...

  9. java hashset 源码_Java集合:HashSet的源码分析

    1 public class HashSet 2 extends AbstractSet 3 implements Set, Cloneable, java.io.Serializable 4{ 5 ...

最新文章

  1. 完美数据迁移-MongoDB Stream的应用
  2. 快速人脸验证--MobileFaceNets: Efficient CNNs for Accurate Real-time Face Verification on Mobile Devices
  3. Zxing二维码开源项目
  4. 2021年中国少儿编程行业报告
  5. 训练日志 2018.9.17
  6. 什么是whois? (whois的概念及应用)
  7. pyspark 条件_删除pyspark中特定条件下的特定行
  8. bootstrap 复选框及单选按钮
  9. ssh secure shell client 保存密码_著名的SSH协议
  10. 决策树后剪枝算法(二)错误率降低剪枝REP
  11. 关于轻量级知识共享平台mm-wiki的使用
  12. Codeforces Round #368 (Div. 2)(C. Pythagorean Triples 勾股数规律)
  13. c语言数码管显示小数点,8位数码管显示正整数和小数及解决鬼影问题
  14. 文件方式实现完整的英文词频统计实例
  15. sysbench官网
  16. 跨国企业在中国 | 麦德龙中国正式引进高品质法国牛肉;雀巢加码在华宠物护理业务...
  17. Pod挂载Volume失败问题分析
  18. RS-232或RS-485与CAN网络互联互通
  19. 俯卧撑和平板支撑,哪个更能塑身呢?有何依据?
  20. Springboot之邮件发送(内附源码)

热门文章

  1. 怎样在半年内把一个团队带垮?
  2. 【中生代技术社区内推】
  3. Jeecg-Boot 快速开发平台,前后端分离—开发环境搭建
  4. jeecg开源社区近期版本发布计划
  5. FireBug 调试JS入门 —如何调试JS
  6. oracle 数据库备份
  7. Shell脚本语言常用命令总结~
  8. 雷林鹏分享:PHP 数组排序
  9. FTP使用外部数据源
  10. python 测试用例 自动生成目录_如何在python中自动将测试用例添加到测试套件中...