@[TOC]
forEach和for in对比
文章思路来自一次dart的踩坑经历,为了下次使用不再纠结。 既然forEach是基于for in (增强型for循环)的封装,那么 forEach 会比 for in 好用吗?
了解forEach、for in
forEach 和 for in 是通过 Iterator 进行遍历 比较下 forEach 的源码,可以看到 forEach 是基于for in (增强型for循环) 的封装,通过回调的方式执行代码块。那么为什么要做这一层封装呢?
/// dart
void forEach(void f(E element)) {
for (E element in this) f(element);
}
// java
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
应用场景?哪个好用?
我们来康康使用场景
void fun(List<Element> list) {
list.forEach((element) ->{
// TODO
// return; 返回给回调函数,也就是上面源码的f()和action 默认只能为void
// 并且不能执行break;跳出
});
}
void fun(List<Element> list) {
for (Element element : list) {
// TODO
// return; 返回到fun函数
// 可以执行break;跳出循环
}
}
可以看到在功能上,明显for in支持的场景更丰富,那forEach的优势呢? 目前想到的场景是,在单线程模型中(如dart)在回调函数里进行异步请求,并且这些请求可以并发的执行,那么forEach的效率就会优于for in。(不可以并发的请求,谨慎使用,谨防踩坑)
dart 踩坑经历
在 dart 中,异步回调最常用的场景就是 await 等待事件返回,继续执行接下来的代码 下面来看两段代码,两段代码分别在子块中执行异步函数,并等待。那么他们的结果会是一样的吗?
Future<void> test1() async {
print('start');
var list = [5,4,3,2,1];
list.forEach((element) async {
await futurePrint(element);
});
print('end');
}
//输出
//start
//end
//1
//2
//3
//4
//5
Future<void> test2() async {
print('start');
var list = [5,4,3,2,1];
for(var t in list) {
await futurePrint(t);
}
print('end');
}
//输出
//start
//5
//4
//3
//2
//1
//end
Future<void> futurePrint(int num) async {
await Future.delayed(Duration(seconds: num));
print(num);
}
结果说明了forEach虽然在子块中添加了await关键字,但是并没有等待,也就是说 子代码块(回调函数)是并行的,而for in则是顺序执行。 那么问题出在哪里了? 我们来看下async关键字的位置
对的,其实for in的await是作用于整个函数的,而forEach里的await是作用于回调函数,也就是源码中的f(E element)。它可以保证在整个回调函数里的执行顺序是有序的。
如果足够细心的话,会发现其实test1()并不需要Future返回值和async关键字,为了看能不能起到“迷惑作用”。(如果在test1()的其他的地方调用异步函数,就可能需要添加这两个关键字,当初也是被这俩“蒙骗了”)