字符串優化處理
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()方法避開了構造方法,快速複製了實例對象。
靜態方法代替實例方法。
實例方法需要維護一張類似虛函數表的結構,實現對多態的支持。