在系統效能最佳化的時候迴圈和字串處理一直是非常值得注意的地方。從心態上我們一定不能把自己的眼界放在十次或者是百次迴圈的層次上,也不能把自己要處理的字串當做是有十個二十個字元。每次遇到迴圈都要假定這個迴圈是上萬次的,每次要處理的字串的時候一定要告訴自己這個字串將來有可能是很大的。不要等到資料量真的達到十萬、百萬的層級之後再採取處理,那樣的話成本的消耗就太大了。本文將介紹關於Java代碼中迴圈和字串的最佳化方法,希望對讀者有用。
關於迴圈
嵌套for迴圈中次數多的放在內側,次數少的放在外側。眾所周知for迴圈需要定義一個迴圈變數來遍曆每一個需要迴圈的對象,那麼如果迴圈次數多的迴圈放在外側那麼無疑將會使得總體的變數增多,效率自然會降低。下面進行代碼測試
public class Test{public static void main (String [] args){Long time2Before=System.nanoTime();for (int i=0; i<10;i++ ){ for (int j=0; j<1000000;j++ ){ }}Long time2After=System.nanoTime();System.out.println("faster--->"+(time2After-time2Before));Long time1Before=System.nanoTime();for (int i=0; i<1000000;i++ ){ for (int j=0; j<10;j++ ){ }}Long time1After=System.nanoTime();System.out.println("slower--->"+(time1After-time1Before));}}
在迴圈中只做與迴圈相關的事情,一些不必要的迴圈不要放到迴圈當中去做。比如在遍曆集合的時候沒有必要將取得集合大小放在迴圈中去做,完全可以放在集合的外邊。效果上沒區別,效能上差距巨大。
import java.util.*;public class Test1{public static void main (String [] args){List<String> list=new ArrayList<String>();for(int i=0;i<1000000;i++){list.add("luck"+i);}Long time1Before=System.nanoTime();for(int i=0;i<list.size();i++){//System.out.println(list.get(i));}Long time1After=System.nanoTime();System.out.println("use .size-->"+(time1After-time1Before));Long time2Before=System.nanoTime();int n=list.size();for(int i=0;i<n;i++){//System.out.println(list.get(i));}Long time2After=System.nanoTime();System.out.println("do not use .size-->"+(time2After-time2Before));}}
關於字串
消除字串串連,在程式中優先考慮使用StringBuffer或者StringBuilder代替String。一個字串相當於一個匿名的String對象,如果在程式中拼接兩個字串那麼會在記憶體中定義三個字串空間。而StringBuffer或者StringBuilder就不會這麼做,而是在原來已有的StringBuffer或者StringBuilder對象中進行修改。測試代碼如下
public class Test3{public static void main (String [] args){long time1Before=System.nanoTime();String str="";for(int i=0;i<10000;i++){str+=i;}long time1After=System.nanoTime();System.out.println("use String ---> "+(time1After-time1Before));long time2Before=System.nanoTime();StringBuilder sbuilder=new StringBuilder();for(int i=0;i<10000;i++){sbuilder.append(i);}long time2After=System.nanoTime();System.out.println("use StringBuilder---> "+(time2After-time2Before));long time3Before=System.nanoTime();StringBuffer stringBuffer=new StringBuffer();for(int i=0;i<10000;i++){stringBuffer.append(i);}long time3After=System.nanoTime();System.out.println("use StringBuffer---> "+(time3After-time3Before));}}
需要說明的是在StringBuffer和StringBuilder之間如果需要考慮選其一的話原則很簡單,前者是安全執行緒的後者是線程不安全的,換句話說後者比前者更快。綜上所述如果單單從效能上考慮的話從高到低依次是:StringBuilder --> StringBuffer --> String。
迴圈和字串是程式中最容易提升代碼效率的地方,因為很多人在寫程式的時候為了圖一時方便將效率拋在腦後,當要處理的資料不大的時候無所謂,一旦程式要處理的資料增大那麼效能的瓶頸也就出現了。所以就像文章開頭所說的,要在寫程式的時候就要考慮十萬百萬的數量級,不要等到效能瓶頸出現再去解決,因為代碼重構或者說後期的最佳化成本要遠遠高於前期的開發成本,相信看過別人無注釋而又冗長代碼的童鞋深有體會(竊笑~~)。