String高效編程優化(Java)

1, substring截取超大字符串可能形成的「內存泄漏」html

2,+ 操做符的優化和侷限算法

3,StringBuilder和StringBuffer編程

4,split和StringTokenizer作簡單字符分割效率的比較數組

 

 

1, substring截取超大字符串可能形成的「內存泄漏」

咱們知道,String對象內保存着一個char數組。可是char數組未必和String所表明的字符集等長,而多是一個「超集」。String有一個私有的構造函數:安全

// Package private constructor which shares value array for speed.
    String(int offset, int count, char value[]) {
        this.value = value;
        this.offset = offset;
        this.count = count;
    }

 

這個構造函數容許你只使用value[]的一部分做爲String的字符集,它並不會截取value[]的一部分來建立一個新的char數組,而是把它整個保存起來了。多線程

接着來看substring函數的實現:app

     public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > count) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        if (beginIndex > endIndex) {
            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
        }
        return ((beginIndex == 0) && (endIndex == count)) ? this :
            
new String(offset + beginIndex, endIndex -
 beginIndex, value);
    }

 

substring正是用咱們上面提到的構造函數來構造返回的String的,Java這麼作有利有弊:框架

1)若是咱們要從一個大字符串中截取許多小字符串,那麼這些小字符串共享一個大的char[]。那麼,這麼作是很是高效的,避免了從新分配內存的時間空間開銷。異步

2)可是,若是咱們只從中截取一個或少數幾個很小的字符串,原String將丟棄,而這些小字符串卻被長期保存,這樣咱們就形成了某種意義上的內存泄漏 -- 咱們覺得原String的內存被GC釋放了,然而並無,它的主要部分 — 巨大的char數組仍被他的子String引用着,雖然只有其中很小的一部分被它們使用了。ide

對於這種泄漏,解決辦法很簡單,使用如下語法

str2 = new String(str1.substring(5,100));

 

構造函數String(String)會爲新的String建立一個新的char[]。可是前提是,咱們意識到了substring可能致使的問題。

 

(後記:

謝謝@╰︶賴牀專業戶こ 的提醒,JDK7早期的版本仍是按照我說的方式實現的,可是後期的版本已經修復了。我沒有注意我參考的版本。
應該說新的實現有利有弊,如今咱們須要擔憂的不是substring的泄露問題,而是效率問題了。
很是讚揚你的研究精神。

 

 

2,+ 操做符的優化和侷限

咱們知道,對於如下語法:

str1 += "abc";
str1 = str1 + "abc";

 

Java將建立一個新的String對象和字符串數組,把原字符串和」abc」拷貝拼接到新的字符串數組中。若是反覆進行這樣字符串的累加操做,天然是很是低效的,這種狀況按照最佳實踐,應該使用StringBuilder。

但事實上,Java已經對+操做進行了優化。看下面的代碼:

String temp = "ABC" + 200 + 'D';

 

編譯器已經把該代碼優化編譯成了:

String temp = new StringBuilder().append( "ABC" ).append( 200 ).append('D').toString();

(注:

另外,若是代碼簡單的多個字符串相加:

String temp = "Hello" + 「 」 + 「World」;

編譯器直接優化爲

String temp = "Hello World」;

 

因此,連續累加效率並不比使用StringBuilder效率差,由於它原本就是用一個StringBuilder對象連續的append來實現的。

可是,若是是:

for(int i=0; i<100; i++)
{
    temp+="abc";
}

 

編譯器並無辦法把以上for循環裏面屢次迭代的‘+’操做優化爲只使用一個StringBuilder對象的連續append操做。所以,仍是很是低效的。

 

簡而言之,若是全部的字符串拼接能夠在一行裏面用‘+’完成,那麼是沒有效率問題的;不然,最好使用StringBuilder。

 

 

 

3,StringBuilder和StringBuffer

StringBuilder和StringBuffer用法基本沒什麼區別,可是StringBuilder不是線程安全的,StringBuffer是線程安全的。StringBuffer在全部用於字符操做的public方法都加了鎖--使用了synchronized關鍵字。

咱們來測試一下單線程下StringBuilder和StringBuffer的效率,如下代碼:

public static void main(String[] args){
        long t1 = System.nanoTime();
        StringBuffer stringBuffer = new StringBuffer();
        
        for(int i=0; i<1000000; i++)
        {
            stringBuffer.append("a");
        }
        stringBuffer.toString();
        long t2 = System.nanoTime();
        System.out.println("StringBuffer :"+ (t2-t1));
        
         t1 = System.nanoTime();
        StringBuilder stringBuilder = new StringBuilder();
        
        for(int i=0; i<1000000; i++)
        {
            stringBuilder.append("a");
        }
        stringBuilder.toString();
         t2 = System.nanoTime();
        System.out.println("StringBuilder:"+ (t2-t1));
    }

結果:

StringBuffer :33979818
StringBuilder:14061978

單線程狀況下,StringBuilder要快一倍多。

 

那多線程狀況StringBuffer效率如何呢?下面代碼測試:

long t1 = System.nanoTime();
        final StringBuffer stringBuffer = new StringBuffer();
        
        ExecutorService executor = Executors.newFixedThreadPool(3);
        CountDownLatch countDownLatch = new CountDownLatch(3);
        
        for (int i = 0; i < 3; i++) {
            executor.execute(new Runnable() {

                @Override
                public void run() {
                    for (int i = 0; i < 333333; i++) {
                        stringBuffer.append("a");
                    }
                }

            });
            countDownLatch.countDown();
        }
        stringBuffer.toString();
        countDownLatch.await();
        
        long t2 = System.nanoTime();
        System.out.println("StringBuffer :"+ (t2-t1));

 

結果:

StringBuffer :2603076

 

雖然咱們使用了3個工做線程,可是效率幾乎比單線程沒有什麼提高,這就是使用鎖在多線程的結果--鎖在多線程中的協調,致使線程的頻繁切換,大大下降效率。

雖然我實在不知道有什麼場景須要用到多線程的字符串拼裝。假設有,而且對性能有很嚴格的要求,我以爲能夠考慮使用一些無鎖的多線程編程框架,例如Disruptor--一個無鎖的RingBuffer框架,使用多個生產者線程往Ring buffer中投遞String對象,在消費者中用StringBuilder進行組裝。(相似log4j 2的異步日誌處理)

 

 

 

 

4,split和StringTokenizer作簡單字符分割效率的比較。

不少文章都說split比StringTokenizer效率高不少,開始也深覺得然,可是卻發現它們的測試代碼都存在很嚴重的問題。本身作了一下測試

        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 1000000; i++) {
            stringBuilder.append(i);
            stringBuilder.append(",");
        }
        
        
        String str = stringBuilder.toString();
        long t1 = System.nanoTime();
        String[] strArray = str.split(",");
        long t2 = System.nanoTime();
        System.out.println("split :" + (t2 - t1));

        String str1 = stringBuilder.toString();
        t1 = System.nanoTime();
        StringTokenizer stringTokenizer = new StringTokenizer(str1, ",");
        //List<String> strList = new ArrayList<String>(1000000); //或者 String[] strArray1 = new String[stringTokenizer.countTokens()];
        for (int i = 0; i < 1000000; i++) {
            String subStr = stringTokenizer.nextToken();
            //strList.add(subStr); //或者strArray1[i] =subStr;
        }
        t2 = System.nanoTime();
        System.out.println("token :" + (t2 - t1));

 

結果:

split :248539389
token :53191452

 

StringTokenizer 比split快4倍。

可是上面的比較在某些狀況下並不公平,split會返回一個數組,而StringTokenizer 的next方法只能逐個瀏覽token。若是要求StringTokenizer 也把返回的子字符串保存在List中,那麼結果如何呢?把上面代碼段中的註釋掉的代碼打開,使StringTokenizer 也要把tokens保存在List或Array中,再進行測試。

結果:

split :254496592
token :303926083

 

這種狀況下StringTokenizer 的效率還差一些。所以,不能一律而論split或StringTokenizer 誰的效率高,還要看若是使用。若是須要把結果放在Array或List當中,split更簡單還有效率。(可見2種算法效率並無本質差異,差就差在Array或List的使用上,具體還要從JDK的源代碼去分析)

 

Binhua Liu原創文章,轉載請註明原地址http://www.cnblogs.com/Binhua-Liu/p/5572350.html

相關文章
相關標籤/搜索