程序優化

字符串優化處理

String對象和特點

  • 不變性
  • 針對常量池的優化
  • 類final定義

String對象

  • char數組
  • offset偏移量
  • count長度

不變性

是指String對象一旦生成,不能改變。當對象需要被多線程共享時,並且訪問頻繁時,可以省略同步和鎖等待時間,從而大幅提升系統性能。

針對常量池的優化

當兩個String對象擁有相同的值時,只引用常量池中的同一個拷貝。當同一個字符串反覆出現時,這個技術可以大量節省內存空間。

類final定義
final類型定義也是String對象的重要特點。作爲final類的String對象在系統中不可能有任何子類,這是對系統安全性的保護。

subString()方法的內存泄露

String的構造函數
String(int offset, int count, char value[]){
this.value = value;
this.offset = offset;
this.count = count;
}

從構造函數看來,這種通過偏移量來獲取值的算法,提高了運算速度,浪費的內存空間。

subString()方法調用String(int offset, int count, char value[])構造函數,此構造函數採用了用空間換時間的手段。但它是一個包內私有的構造函數,應用程序無法訪問它。

字符串分割和查找

字符串分割String中的split()方法實現了這個功能。

public String[] split(String regex)

傳入參數可以是正則表達式,從而進行復雜邏輯的字符串分割。

"a;b,c:d".split("[;|,|:]");

StringTokenizer類是JDK中提供的專門用來處理字符串分割子串的工具類。

public StringTokenizer(String str, String delim)

更優化的字符串分割方式

indexOf()和subString()

String tmp = anyString;
for(int i=0;i<10000;i++){
while(true){
    String splitStr = null;
    int j = tmp.indexOf(';');
    if(j<0)break;
    splitStr = tmp.substring(0,j);
tmp=tmp.substring(j+1); 
}
tmp = anyString;
}

StringBuffer和StringBuilder

StringBuffer與StringBuilder是java.lang包下被大家熟知的兩個類。其異同爲:一、長度都是可擴充的;二、StringBuffer是線程安全的,StringBuilder是線程不安全的。

StringBuilder和StringBuffer的選擇

StringBuffer因爲實現了方法同步,所以是線程安全的。但是方法同步則需要消耗一定的系統資源。StringBuilder的效率好過於StringBuffer。

從實現的角度來說,StringBuffer所有方法(構造方法除外,因爲沒有必要)簽名中都使用 synchronized 限定,也就是所有的方法都是同步的。

容量參數

無論是StringBuilder或者是StringBuffer,初始化時都要設置一個容量參數,默認是16個字節。需要擴容時翻倍。

List接口

List是重要的數據結構之一,ArrayList,Vector和LinkedList。

ArrayList和Vector用數組實現,封裝了對數組的操作。ArrayList並不是線程同步,vector是線程同步的。

LinkedList使用了循環雙向鏈表數據結構。LinkedList使用了表項,包括元素內容,前驅表項和後驅表項。

ArrayList的add()方法取決於ensureCapacity()方法,實現是如果需要擴容增到原先容量的1.5倍。

LinkedList使用了鏈表結構,不需要維護容量大小,每次增加一個元素,需要新建一個entry對象,並進行賦值操作,在頻繁的系統調用中,必然會性能降低。

插入數據時,ArrayList由於是數組實現,任意位置插入元素,元素所有元素需要重新排列。效率遠不如LinkedList。

ArrayList的remove數據與add數據時相同。LinkedList則需要先定位要刪除的元素,分爲前後半段。前半段從前往後找,後半段從後往前找。中間的時候效率最低。

遍歷列表

ForEach,迭代器和For循環。反編譯ForEach和迭代器是等價的。ForEach的性能略低於迭代器。

Map接口

HashMap和HashTable有兩套不同的實現,HashTable大部分方法做了同步,不允許key或者value的值爲null。

HashMap的實現原理

HashMap就是將key做hash算法,然後將hash值映射到內存地址,直接取得key對應的數據。底層數據結構使用的是數,所謂的內存地址即數組的下標索引。

Hash衝突

HashMap內部維護一個Entry數組,每一個Entry表項包括key,value,next和hash幾項。

Set接口

Set是對map的封裝。

RandomAccess

RandomAccess接口是一個標誌接口,本身沒有任何方法。任何實現該接口的對象都可以認爲是支持快速隨機訪問的對象。

JDK實現中,基於數組的列表實現了RandomAccess接口。

使用NIO提升性能

Java的標準IO中,New IO。基於塊爲基本單位處理數據。最重要的組件是緩衝Buffer和通道Channel。緩衝是一塊連續的內存塊,是NIO讀寫數據的中轉地。通道表示緩衝數據的源頭或者目的地。

Buffer的基本原理

三個重要參數:位置,容量和上限。
Buffer創建

//從堆中分配
ByteBuffer buffer = ByteBuffer.allocate(1024);

//從既有數組中創建
byte array[] = new byte[1024];
ByteBuffer buffer = ByteBuffer.wrap(array);

重置和清空緩衝區

public final Buffer rewind();
public final Buffer clear();
public final Buffer flip();

重置Buffer對象,重置Buffer各項標誌位,並不清空Buffer的內容。緩衝區分片使用slice()方法實現,現有緩衝區內換件子緩衝區,子緩衝區和父緩衝區共享數據。通過slice方法,將系統模塊化,分割出的子緩衝區具有完整的緩衝區模型。

文件映射到內存FileChannel.map()
將文件代碼前1024字節映射到內存中。

處理結構化數據
NIO提供散射和聚集。散射指的是將數據讀入一組Buffer中,而不僅是一個。聚集將數據寫入一組Buffer中。

引用類型

4個級別的引用類型:強引用,軟引用,弱引用,虛引用。

變量在棧中的局部變量表中指向堆空間的引用實例的地址。強引用指向的對象不會被JVM回收。強引用可能導致OOM。
軟引用使用java.lang.ref.SoftReference使用軟引用。當堆使用率臨近閾值時,JVM纔回收軟引用對象。當系統GC時,直接回收弱引用。

WeakHashMap類
自動釋放已經被回收的key所在的表項。但如果WeakHashMap的key在系統中持有強引用,就退化爲HashMap,所有表項無法清除。

慎用異常。Try-Catch語句循環影響系統性能。

使用局部變量。方法的傳參和創建的臨時變量都保存在棧中。靜態變量和實例變量在堆中創建。

位運算代替乘除法。Switch替換爲if else

一維數組代替二維數組。提取數組長度到變量。

提取表達式。布爾運算代替位運算。

使用arraycopy()。使用Bufffer進行IO操作。

clone()方法避開了構造方法,快速複製了實例對象。

靜態方法代替實例方法。
實例方法需要維護一張類似虛函數表的結構,實現對多態的支持。

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