for-each 循环优先于传统的for循环-- Joshua Blochhtmla hand-written counted loop is better than for the enhanced loop. -- jackiemliujava
本文仅限于 ArrayList,LinkedList 不在讨论话题内。android
首先,相信你们对于这两种循环都很熟悉:缓存
// foreach
for (Box box : boxList)
box.id++;
// index for(记住保存len,不然每次都须要调用boxList.size())
for (int index = 0,len = boxList.size(); index < len; index++)
boxList.get(index).id++;复制代码
但二者什么时候使用,性能和效率怎么权衡,又成为了新的问题!性能优化
所以这篇文章就主要是针对这两种循环方式和 Android 平台上的取舍作一些简单的分析。架构
先给出最佳实践,缘由后面进行分析:并发
一、优先使用 index-for 模式(Android Framework 推荐);oop
二、若是想在遍历过程当中暴露出其它线程正在修改(ConcurrentModificationException)的问题,请使用 for-each 模式.性能
for each这种写法实际上是一个语法糖,其实优化
for(Box box:boxList)
等同于 for(Iterator var1 = boxList.iterator(); var1.hasNext();var1.next())
能够看到这种模式会去额外生成一个 Iterator 对象,因此相较于 Index 模式而言,它会额外使用一些内存。
在 Android 平台,内存资源是极为有限的,若是只是单层的循环还算 OK,可是若是是多层循环,
或者是隐式的多层循环中使用 Iterate 模式,可想而知内存会临时分配不少个变量。
例如:
// 显式多层循环
for (Box box : boxList)
for (InnerBox innerBox : box)
; // do something
// 隐式的多层循环(View 的 onDraw 方法)
void onDraw(Canvas c){
for (Box box : boxList)
; // do something
}复制代码
以上这两种状况可能来讲就会分配过多的临时对象,致使内存不足进行 GC ,从而影响 App 流畅度。
除此以外,在迭代的过程当中,会去调用 Iterator 的 next()
,这里我以 ArrayList 为例:
public E next() {
checkForComodification(); // 检查是否在遍历过程当中有人修改了列表
int i = cursor;
if (i >= size) // 检查下标是否合法
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) // 检查下标是否合法
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i]; // 返回对象
}复制代码
相比与Index 模式,它增长了不少检查,因此会带来必定的开销,但也是一种特性(检查遍历中是否有人修改)。
你们最多见的多是:
for(int index = 0; index < boxList.size();index++)
; // do something复制代码
但这种方式其实并不够理想,由于每次循环时,都会去调用 List.size()。
因此咱们能够将其保存下来:
for(int index = 0,len = boxList.size(); index < len;index++)
; // do something复制代码
相对而言,保存 len 的方法更快,尤为在 Effective Java 中的范例:
for(int index = 0,n = expensiveComputation(); index < n;index++)
; // do something复制代码
若是一个方法耗时较多且结果不会改变,那么能够用一个临时变量充当缓存。
一开始我还本身在电脑上作了个小 demo,然而发现 Android Team 已经作了一个更具表明性的(屡次取平均值),这里直接借用吧:
这里的案例是 400,000 随机的 Integer,每种模式都跑 10 次,去掉最大最小值后取平均结果。
能够很明显看出 Index 模式的耗时完美胜出,Yeah |ω・)
因为以前介绍了 Iterate 模式会初始化一个 Iterator 对象,因此它的内存占用确定多于 Index 模式。
0、语法糖可能很甜,但也可能有隐藏的性能损耗(e.g lambda 表达式会增长运行时开销)
一、从语法上而言,foreach 这种更简洁,也更加地隐藏了细节(语法糖),但也缺失了某些特性。
简单来讲,尽量将 foreach 当作是一种 只读向后遍历。
在 Effective Java 中,介绍了没法使用的三种场景:
二、开发 Android 的时候,尽量放弃使用 foreach ,减小内存压力。
三、若是嫌弃 Index 模式的模板代码太麻烦,能够试试 Live Templates 中自带的 itli ,一键生成循环代码哦~
四、在写 Index 模式的时候,尽量去保存 list.size(),虽然 JIT 有可能会进行优化,但这种方式能够更加保险。
五、性能优化不仅是总体架构或者类库的优化,也要从平时点滴作起。
最后,推荐你们看看: Performance Tips