1、以下一段再平常不過的遍歷代碼,但是與我一樣,好多新手都會在這個地方出問題,例如
for(int i=0; i<list.size();i++) // 執行;
之前我在剛工作的時候在這個地方犯錯,我們在自測的時候都沒問題。初始化數據之后給客戶演示時,突然蹦出個空指針異常NullPointerException。
于是我們通過debug發現,這個list為空,平常我們開發都會手工插入一些數據到表,自然不會報空,演示的時候數據表為空,自然就報錯了。改進:
for(int i=0; list!=null&&i<list.size(); i++) //執行;
不會報錯了吧!
但是有經驗的開發人員會發現for循環,每次都會去對循環條件進行判斷,于是繼續改進
if(list!=null && !list.isEmpty()) for(int i=0; i<list.size(); i++) // 執行;
還有問題嗎
我們會發現循環還是每次都會去執行list.size();方法,list.size()方法也就是一句代碼return size;看似簡單的一句代碼,但是我們知道每次執行一個方法JVM都會為該方法申請一個棧幀,過程雖短,但是也是一項不小的開銷。于是繼續改進
if(list!=null && !list.isEmpty()) for(int i=0,len=list.size(); i<len; i++) // 執行;
這樣效率真的會高一些嗎,實驗是最好的答案
List<Integer> list = new ArrayList<Integer>();for(int i=0; i<30000000;i++)list.add(i);// 普通循環long t1 = System.currentTimeMillis();for(int j=0; j<100; j++) for(int i=0; i<list.size(); i++) { Integer it1 = list.get(i); }System.out.PRintln(System.currentTimeMillis()-t1);// 改進lent1 = System.currentTimeMillis();for(int j=0; j<100; j++) for(int i=0, len=list.size(); i<len; i++) { Integer it2 = list.get(i); }System.out.println(System.currentTimeMillis()-t1);// foreach方式遍歷t1 = System.currentTimeMillis();for(int j=0; j<100; j++) for(Integer it4: list) { }System.out.println(System.currentTimeMillis()-t1);結果普通for結果 22335使用len結果 14369使用foreach結果 29163經過多次測試發現,普通for結果比len會多出三分之一左右,foreach效果比普通for會略差,而且經過多次測試發現一個有趣的現象,循環位于不同的位置,結果也會有所差距,貌似JVM對循環過的集合會進行優化,下一次再次循環這個集合,速度會有所加快,這個大家也可以去做一下實驗,集合過大的時候可能報堆溢出,可以通過修改。-Xms128M -Xmx512M
PS:以上遍歷方法不考慮多線程不會有什么問題,如果在多線程下遍歷將len提出來,就容易出錯了,因為len就獲取一次。
2、接下來考慮一個問題,多線程下需要同步遍歷某個公共集合,如何實現我們可能會想到用同步塊synchronized
synchronized(list) { if(list!=null && !list.isEmpty()) for(int i=0,len=list.size(); i<len; i++) 執行;} 這樣的代碼真的好嗎?我們可以想一想,多線程的情況下,你將公共的list一鎖,其他的線程想插入list刪除list只能干等著,更嚴重的是如果你在遍歷的時候還執行一些長時間的操作,例如關閉連接或者啟動監聽等等。于是我們就想如何能夠讓等待的時間盡量的短,我們完全可以先將集合克隆下來(至于深淺克隆根據具體情況而論),再去執行,當然如果循環執行時間很短,完全沒這個必要。
LifecycleListener[] interested = null;// 用克隆減少時間,因為如果一個一個去觸發事件響應,得使用大量時間,而又因為線程鎖的存在,使得其他線程必須繼續等待synchronized (listeners) { interested = (LifecycleListener[])listeners.clone();}// 逐個觸發事件響應for(int i=0; i<interested.length; i++) { interested[i].lifecycleEvent(event);}為什么上面沒有使用len中間變量,因為數組的length并不會占用執行時間。
新聞熱點
疑難解答