改善性能的技巧篇

慎用異常

在java軟件開發中,經常使用try-catch進行錯誤捕獲異常,但是try-catch語句對系統性能而言是非常糟糕的,雖然再一次try-catch中無法察覺它帶來的損失,但是,一旦try-catch語句被應用於循環中,就會給系統帶來極大的傷害。
使用實例

使用局部變量

調用方法時,傳遞的參數以及在調用中創建的臨時變量都保存在棧(Stack)中,速度較快。
其他變量,如靜態變量、實例變量等,都在堆(heap)創建速度較慢。
局部變量

位運算代替乘除法

在所有運算中,位運算是最高效的運算。
位運算實例

一維數組代替二維數組

由於數組的隨機訪問非常好,許多JDK類庫,如ArrayList、Vector等都使用了數組作爲其底層實現,但是一維數組和二維數組的訪問速度是不一樣的,一維數組的訪問速度要優於二維數組。因此在性能敏感的系統中要使用二維數組的,可以通過可靠的算法,將二維數組轉爲一維數組在進行處理,以提高系統的響應速度。
二維數組定義:
int [ ][ ] arr=new int [5][3]; 表示五個一維數組,每個一維數組長度是3.這就組成了二維數組,即5行3列。

優化方式:把二維數組用一維數組替換,提取公共變量

這是一個二維數組
public class Demo {
	public static void main(String[] args) throws Exception {
		int array[][]=new int[10][5];
		
		for(int i=0;i<array.length;i++) {
			for(int j=0;j<array[0].length;j++){
				array[i][j]=i;
			}
		}
	}
}

把它變成等價的一維數組,並提取公共變量
public class Demo {
	
	public static void main(String[] args) throws Exception {
		int array[]=new int[50];
		int len=array.length;
		for(int i=0;i<len;i++) {
			array[i]=i;
		}
		System.out.print(Arrays.toString(array));
	}
}
優化完畢。
計算的時候我們也應該注意,提取重複的表達式、公因式等操作。

展開循環

普通的循環:
public class Demo {
	
	public static void main(String[] args) throws Exception {
		int arr[]=new int[10];
		for(int i=0;i<10;i++) {
			arr[i]=i;
		}
		System.out.println(Arrays.toString(arr));
	}
}
結果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

展開循環
public class Demo {
	
	public static void main(String[] args) throws Exception {
		int arr[]=new int[10];
		for(int i=0;i<10;i+=2) {
			arr[i]=i;
			arr[i+1]=i+1;
		}
		System.out.println(Arrays.toString(arr));
	}
}
結果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
展開循環後,系統性能提升。效率提高。

使用arrayCopy

數組複製是一項使用頻率很高的功能,JDK中提供了一個高效的API來實現它
public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
這個是System下的一個方法。
java本地方法,所以效率更高。以後數組複製就使用這個方法。(不要自己去實現)

public class Demo {
	
	public static void main(String[] args) throws Exception {
		long start=System.currentTimeMillis();
		int arr[]=new int[1000000];
		int arrs[]=new int[1000000];
		for(int i=0;i<1000000;i+=2) {
			arr[i]=i;
			arr[i+1]=i+1;
		}
		for(int j=0;j<arr.length;j++) {
			arrs[j]=arr[j];
		}
		System.out.println(System.currentTimeMillis()-start);
	}
}
結果用時:9

public class Demo {
	
	public static void main(String[] args) throws Exception {
		long start=System.currentTimeMillis();
		int arr[]=new int[1000000];
		int arrs[]=new int[1000000];
		for(int i=0;i<1000000;i+=2) {
			arr[i]=i;
			arr[i+1]=i+1;
		}
		System.arraycopy(arr, 0, arrs, 0, 1000000);
		
		System.out.println(System.currentTimeMillis()-start);
	}
}
結果用時:6
從結果可以看出小數據量就可以發現差距。所以用本地方法System.arraycopy來進行數組複製,
效率是最好的。

使用Buffer進行I/O操作

除了NIO之外,使用java進行I/O操作有兩種基本方式:

  • 使用基於InputStream和OutputStream的方式
  • 使用Writer和Reader

無論使用哪種方式進行文件I/O,如果能合理的使用緩衝,就能有效的提高I/O的性能,緩衝配套組件如下:
java操作I/O的基本操作方法

public class Demo {
	public static void main(String[] args) throws Exception {
		long start=System.currentTimeMillis();
		
		String path="d:"+File.separator+"book.txt";
		File file=new File(path);
		FileOutputStream fos=new FileOutputStream(file);
		DataOutputStream dos=new DataOutputStream(fos);
		for(int i=0;i<10000;i++) {
			dos.write(i);
		}
		dos.close();
		fos.close();
		
		System.out.println(System.currentTimeMillis()-start);
	}
}
結果時長:168
public class Demo {	
	public static void main(String[] args) throws Exception {
		long start=System.currentTimeMillis();
		
		String path="d:"+File.separator+"book.txt";
		File file=new File(path);
		FileOutputStream fos=new FileOutputStream(file);
		DataOutputStream dos=new DataOutputStream(new BufferedOutputStream(fos));
		for(int i=0;i<10000;i++) {
			dos.write(i);
		}
		dos.close();
		fos.close();
		
		System.out.println(System.currentTimeMillis()-start);
	}
}
加上緩衝之後的時長:1

所以緩衝其實不論是在寫文件還是讀文件,對性能都有很大的提升。Reader和Writer的實例就不列舉了,
使用緩衝效果都很明顯。

使用clone()代替new

在java中新建對象實例最常用的方法是 使用new關鍵字,JDK對new關鍵字的支持非常好,使用new關鍵字創建輕量級對象時,速度非常快。但是對於重量級對象,由於對象在構造函數中可能會進行一些複雜且耗時的操作,因此構造函數的執行時間可能會比較長,同時也使得系統無法在短期內或得大量實例,這時,我們可以使用Object.clone()方法。
Object.clone()方法可以繞過對象構造函數,快速複製一個對象實例,由於不需要調用對象構造函數,因此clone方法不會受到構造函數的性能影響,所以快速生成一個實例,但是在默認情況下,clone()方法的生成只是原對象的淺拷貝,如果需要深拷貝,則需要重新實現clone方法。

靜態方法代替實例方法

使用static關鍵字描述的方法爲靜態方法。在java中由於實例方法需要維護一張類似虛函數表的結構,以實現對多態的支持。與靜態方法相比,實例方法的調用,需要更多的資源。因此對於常用的工具類,沒有對其進行重載的必要,那麼將它們聲明爲static,便可以加速方法的調用。
對於一些工具類,應該使用static方法實現,這樣不僅可以加快函數調用的速度,同時,調用static方法也不需要生成類的實例,比調用實例方法更爲方便、易用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章