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