1. 程式人生 > >編寫高質量程式碼:改善Java程式的151個建議(第4章:字串___建議52~59)

編寫高質量程式碼:改善Java程式的151個建議(第4章:字串___建議52~59)

生活不只眼前的苟且。還有讀不懂的詩和到不了的遠方。 --閆妮

建議52:推薦使用String直接賦值

建議53:注意方法中傳遞的引數要求

建議54:正確使用String、StringBuffer、StringBuilder

建議55:注意字串的位置

建議56:自由選擇字串的拼接方法

建議57:推薦在複雜字串操作中使用正則表示式

建議58:強烈建議使用UTF編碼

建議59:對字串持有一種寬容的心態

建議52:推薦使用String直接賦值

建議53:注意方法中傳遞的引數要求

建議54:正確使用String、StringBuffer、StringBuilder

1、Java String類

字串廣泛應用在Java程式設計中,在Java中字串屬於物件,Java提供了String類來建立和操作字串。

需要注意的是String的值是不可變的,這就導致每次對String的操作都會生成新的String物件,這樣不僅效率低下,而且浪費有限的記憶體空間。

我們可以看到,初始String值為“hello”,然後在這個字串後面加上新的字串“world”,這個過程是需要重新在棧堆記憶體中開闢記憶體空間的,最終得到了“hello world”字串也相應的需要開闢記憶體空間,這樣短短的兩個字串,卻需要開闢三次記憶體空間,不得不說這是對記憶體空間的極大浪費。為了應對經常性的字串相關的操作,Java引入了兩個新的類——StringBuffer類和StringBuild類來對此種變化字串進行處理。

2、StringBuffer 和 StringBuilder 類

三者區別:

string:不可變字元序列

StringBuffer:可變字元序列、執行緒安全、效率低

StringBuilder:可變字元序列、執行緒不安全、效率高

注:

String的使用陷阱:

String  ss = "a";

ss +="b";

如果多次執行這些改變字串內容的操作,會導致大量副本字串物件存在記憶體中,降低效率。如果這樣的操作放在迴圈中,會極大影響程式的效能。

3、效能測試

String的拼接與StringBuilder的使用對比,簡單的12個字串的迴圈解析拼接,相差了28毫秒,沒有對比就沒有傷害,以後用StringBuilder吧,暫時還不知道這個東西會有什麼弊端,持續觀察吧

ee4f3054569c9dee6d67fe1f0b1f096dda7.jpg

建議55:注意字串的位置

public class Client55 {
    public static void main(String[] args) {
        String str1 = 1 + 2 + "apples";
        String str2 = "apples" + 1 + 2;
        System.out.println(str1);
        System.out.println(str2);
    }
}

答案是不一致,str1的值是"3apples" ,str2的值是“apples12”。

建議56:自由選擇字串的拼接方法

package OSChina.Client;

public class Client1 {
    public static void main(String[] args) {
        // 加號拼接
        String str = "";
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            str += "江疏影";
        }
        long end1 = System.currentTimeMillis();
        System.out.println("加號拼接耗時:" + (end1 - start1) + "ms");

        // concat拼接
        str = "";
        long start2 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            str = str.concat("江疏影");
        }
        long end2 = System.currentTimeMillis();
        System.out.println("concat拼接耗時:" + (end2 - start2) + "ms");

        // StringBuilder拼接
        str = "";
        StringBuilder buffer = new StringBuilder("");
        long start3 = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            buffer.append("江疏影");
        }
        long end3 = System.currentTimeMillis();
        System.out.println("StringBuilder拼接耗時:" + (end3 - start3) + "ms");

        // StringBuffer拼接
        str = "";
        StringBuffer sb = new StringBuffer("");
        long start4 = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            sb.append("江疏影");
        }
        long end4 = System.currentTimeMillis();
        System.out.println("StringBuffer拼接耗時:" + (end4 - start4) + "ms");
    }
}

從上面的執行結果來看,在字串拼接方式中,StringBuilder的append方法最快,StringBuffer的append方法次之(因為StringBuffer的append方法是執行緒安全的,同步方法自然慢一點),其次是concat方法,加號最慢,這是為何呢?

速度:StringBuilder>StringBuffer>concat>String+

原理:

1、“+”方法拼接字串:

str= new StringBuilder(str).append("c").toString();

它與純粹使用StringBuilder的append方法是不同的:一是每次迴圈都會建立一個StringBuilder物件,二是每次執行完畢都要呼叫toString方法將其轉換為字串——它的執行時間就耗費在這裡了!

2、concat方法拼接字串:我們從原始碼上看一下concat方法的實現,程式碼如下:

    public String concat(String str) {
        int otherLen = str.length();
        //如果追加字元長度為0,則返回字串本身
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        //產生一個新的字串
        return new String(buf, true);
    }

其整體看上去就是一個數組拷貝,雖然在記憶體中處理都是原子性操作,速度非常快,不過,注意看最後的return語句,每次concat操作都會建立一個String物件,這就是concat速度慢下來的真正原因,它建立了10萬個String物件呀。

3、append方法拼接字串:StringBuilder的append方法直接由父類AbstractStringBuilder實現,其程式碼如下:

public AbstractStringBuilder append(String str) {
   //如果是null值,則把null作為字串處理
   if (str == null) str = "null";
   int len = str.length();
   ensureCapacityInternal(count + len);
   //字串複製到目標陣列
   str.getChars(0, len, value, count);
   count += len;
   return this;
}

整個append方法都在做字元陣列處理,加長,然後拷貝陣列,這些都是基本的資料處理,沒有建立任何物件,所以速度也就最快。

4、StringBuffer的處理和此類似,只是方法是同步的而已。

建議57:推薦在複雜字串操作中使用正則表示式

建議58:強烈建議使用UTF編碼

注:一個系統使用統一的編碼。

建議59:對字串持有一種寬容的心態

中文的排序問題很混亂,Java使用UNICODE編碼,而中文UNICODE字符集來源於GB2312,GB2312是一個包含了7000多個字元的字符集,它是按照拼音排序,並且是連續的,之後的GBK、GB18030都是在其基礎上擴充而來的,所以要讓它們完整的排序也就難上加難了。

如果排序物件是經常使用的漢字,使用Collator類排序完全可以滿足我們的要求,畢竟GB2312已經包含了大部分的漢字,如果需要嚴格排序,則要使用一些開源專案來自己實現了。

注意:如果排序不是一個關鍵演算法,使用Collator類即可。

 

編寫高質量程式碼:改善Java程式的151個建