今天我们介绍关于 List 和 Iterable 里有趣的知识点 ,你可能会觉得这有什么好介绍,不就是列表吗?但是其实在 Dart 里 List 和 Iterable 也是很有意思设定,比如有时候我们可以对 List 进行 map 操作,如下代码所示,你觉得运行之后会打印出什么内容?
var list = ["1", "2", "3", "4", "5"];brvar map = list.map((e) {br var result = int.parse(e) + 10;br print("######### $result");br return result;br});
答案是:什么都不会输出,因为通过 List 返回一个 Iterable 的操作(如 map \ where)的都是 Lazy 的,也就是它们只会在每次“迭代”时才会被调用。
比如调用 toList(); 或者 toString(); 等方法,就会触发上面的 map 执行,从而打印出对应的内容,那新问题来了,假如我们把下图四个方法都执行一遍,会输出几次 log ?em····答案是 3 次。
其中除了 isEmpty 之外,其他的三个操作都会重新触发 map 方法的执行,那究竟是为什么呢?
其实当我们对一个 List 进行 map 等操作时,返回的是一个 Iterable 的 Lazy 对象,而每当我们需要访问里面 value 时, Iterable 都会重新执行一遍操作,因为它不会对上次操作的结果进行缓存记录。
是不是有点懵?这里借用
fast_immutable_collections 作者的一个例子来介绍可能更会清晰,如下代码所示:
var lazyCounter = 0;brvar eagerCounter = 0;brbrvar lazyOddFilter = [1, 2, 3, 4, 5, 6, 7].where((i) {br lazyCounter++;br return i % 2 == 0;br});brbrvar evenFilterEager = [1, 2, 3, 4, 5, 6, 7].where((i) {br eagerCounter++;br return i % 2 == 0;br}).toList();brbrprint("\n\n---------- Init ----------\n\n");brbrlazyOddFilter.length;brlazyOddFilter.length;brlazyOddFilter.length;brbrevenFilterEager.length;brevenFilterEager.length;brevenFilterEager.length;brbrprint("\n\n---------- Lazy vs Eager ----------\n\n");brbrprint("Lazy: $lazyCounter");brprint("Eager: $eagerCounter");brbrprint("\n\n---------- END ----------\n\n");
如下图所示,这个例子最终会输出 Lazy: 21 Eager: 7 这样的结果:
到这里你应该理解了 Iterable 的 Lazy 性质的特殊之处了吧?
那接下来看一个升级的例子,如下代码所示,我们依然是分了 eager 和 lazy 两组做对比,只是这次我们在 where 里添加了判断条件,并且做了嵌套调用,那么你觉得输出结果会是什么?
brbrList<int> removeOdd_eager(Iterable<int> source) {br return source.where((i) {br print("removeOdd_eager");br return i % 2 == 0;br }).toList();br}brbrList<int> removeLessThan10_eager(Iterable<int> source) {br return source.where((i) {br print("removeLessThan10_eager");br return i >= 10;br }).toList();br}brbrIterable<int> removeOdd_lazy(Iterable<int> source) {br return source.where((i) {br print("removeOdd_lazy");br return i % 2 == 0;br });br}brbrIterable<int> removeLessThan10_lazy(Iterable<int> source) {br return source.where((i) {br print("removeLessThan10_lazy");br return i >= 10;br });br}brbrvar list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];brbrprint("\n\n---------- Init ----------\n\n");brbrIterable<int> eager = removeLessThan10_eager(removeOdd_eager(list));brbrIterable<int> lazy = removeLessThan10_lazy(removeOdd_lazy(list));brbrprint("\n\n---------- Lazy ----------\n\n");brbrprint(lazy);brbrprint("\n\n---------- Eager ----------\n\n");brbrprint(eager);
如下所示,可以看到 :
brI/flutter (23298): ---------- Init ----------brI/flutter (23298): brI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeOdd_eagerbrI/flutter (23298): removeLessThan10_eagerbrI/flutter (23298): removeLessThan10_eagerbrI/flutter (23298): removeLessThan10_eagerbrI/flutter (23298): removeLessThan10_eagerbrI/flutter (23298): removeLessThan10_eagerbrI/flutter (23298): removeLessThan10_eagerbrI/flutter (23298): removeLessThan10_eagerbrI/flutter (23298): ---------- Lazy ----------brI/flutter (23298): brI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeLessThan10_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeLessThan10_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeLessThan10_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeLessThan10_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeLessThan10_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeLessThan10_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): removeLessThan10_lazybrI/flutter (23298): removeOdd_lazybrI/flutter (23298): (10, 12, 14)brI/flutter (23298): ---------- Eager ----------brI/flutter (23298): brI/flutter (23298): [10, 12, 14]
是不是很觉得,这种时候 Iterable 把事情变得很复杂? 确实在这种复杂嵌套的时候, Iterable 会把逻辑变得很难维护,而官方也表示:
由于 Iterable 可能被多次迭代,因此不建议在迭代器中使用 side-effects 。
那了解 Iterable 有什么用?或者说 Iterable 可以用在什么场景?其实还是不少, 例如:
举个例子,如下代码所示,感受下 naturalsFunc 这里 Iterable 配合 Stream 为什么可以正常:
brIterable<int> naturalsFunc() sync* {br int k = 0;br // Infinite loop!br while (true) yield k++;br}brbrvar naturalsIter = naturalsFunc();brbrprint("\n\n---------- Init ----------\n\n");brprint("The infinite list/iterable was created, but not evaluated.");brprint("\n\n--------------------\n\n");brprint("\n\n---------- takeWhile ----------\n\n");brprint("It's possible to work with it,"br "but it's necessary to add a method to "br "stop the processing at some point");brvar naturalsUpTo10 = naturalsIter.takeWhile((value) => value <= 10);brprint("Naturals up to 10: $naturalsUpTo10");brprint("\n\n---------- END ----------\n\n");
那到这里你可能会问:List 不也是 Iterable 么,它和 map、where 、expand 等操作返回的 Iterable 又有什么区别 ?
如果我们看 List 本身,你会看到它是一个 abstract 对象,它作为 Iterable 的子类,其实一般情况下实现对象会是 dart vm 里的 _GrowableList,而 _GrowableList 的结构关系如下图所示:
而 List 和其他 Iterable 的不同在于在于:
最后做个总结:本篇的知识点很单一,内容也很简单,就是带大家快速感受下 List 和一般 Iterable 的区别,并且通过例子理解 Iterable 懒加载的特性和应用场景,这样有利于在开发过程中 Iterable 进行选型和问题定位。
如果你还有什么疑惑,欢迎留言评论。