java基礎解析系列(一)---String、StringBuffer、StringBuilder


java基礎解析系列(一)---String、StringBuffer、StringBuilder

前言:本系列的主題是平時容易疏忽的知識點,只有基礎扎實,在編碼的時候才能更注重規范和性能,在出現bug的時候,才能處理更加從容。

我的博客目錄

String

==問題

        String s6=new String("jiajun");
        String s1="jiajun";
        String s2="jiajun";
        System.out.println(s1==s2);//true
        System.out.println(s1==s6);//false
  • 看常量池中是否已有此字符串,如果有,將指針指向這個字符串
  • 如果使用new來創建字符串對象,那么這個字符串是存放在堆中,無論堆中是否已有這個對象

String對象改變

public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }
    
    public String concat(String str) {
        int otherLen = str.length();
        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);
    }
  • 從源碼可以看出,任何操作都是創建一個新的對象,不影響原對象

StringBuffer和StringBuidler

初始容量

  • StringBuilder和StringBuffer的構造參數來初始化容量
public StringBuilder() {
        super(16);
    }
AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
  • 默認情況下容量為16
public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
  • 從源碼看出,在執行append方法的時候,會執行ensureCapacityInternal方法來保證容量,而如果超出容量的話,會重新創建一個char數組,並將舊的字符數組復制到新的字符數組

線程安全

public synchronized StringBuffer append(StringBuffer sb) {
        toStringCache = null;
        super.append(sb);
        return this;
    }
 public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    
 public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
  • 可以看出,String的方法是加了synchronzied,也就加了鎖,那么而在單線程的情況下或者不用考慮線程安全的情況下,那么StringBuilder的性能是更高的

toString方法

 public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
  • 通過源碼發現,toString方法會創建一個新的String對象

性能試驗

間接相加和直接相加

public class d {
	public static void main(String[] args) {
		String s="I"+"love"+"jiajun";
		String s1="I";
		String s2="love";
		String s3="jiajun";
		String s4=s1+s2+s3;
	}
}

  • 通過反編譯的結果可以看出,第一種方式字符串直接相加,在編譯器就直接優化了”Ilovejiajun“
  • 而第二種方式間接相加,從結果可以看出,是先創建一個StringBuilder,然后再apend,最后再toString方法,可以發現性能比第一種低
public class d {
	public static void main(String[] args) {
		String s="I"+"love"+"jiajun";
		String s1="I";
		String s2=s1+"lovejiajun";
		System.out.println(s==s2);
	}
}

  • 同樣從反編譯的結果可以看出,第二種方式並沒有被優化,也是通過StringBuilder來實現的,最后通過toString方法創建一個String對象,所以返回的false
  • 但是當s1是用final修飾的卻是不一樣的,虛擬機會對其進行優化,所以不會像之前一樣創建一個StringBuilder,最后在堆中產生一個對象
public class d {
	public static void main(String[] args) {
		
		final String s1="I";
		
		String s2=s1+"lovejiajun";
	    String s3="Ilovejiajun";
	    //s1==s3
	}
}

用+和用append

public class Demo3 {
	public static void main(String[] args) {
		run1();
		run2();
	}
	
	public static void run1() {
        long start = System.currentTimeMillis();
        String result = "";
        for (int i = 0; i < 10000; i++) {
            result += i;
        }
        System.out.println(System.currentTimeMillis() - start);
    }

    public static void run2() {
         long start = System.currentTimeMillis();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            builder.append(i);
        }
        System.out.println(System.currentTimeMillis() - start);
    }
    //輸出:223 1

  • 從實驗發現,用append效率更高,從實驗一發現,當字符串相加的時候,實際上每次都會重新初始化StringBuilder然后執行相加,這樣效率並不高

初始化容量

public class Demo3 {
	public static void main(String[] args) {
		test1();
		test2();
	}

	public static void test1() {
		StringBuilder sb = new StringBuilder(7000000);
		long start = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			sb.append("jiajun");
		}
		long end=System.currentTimeMillis()-start;
		System.out.println(end);
	}

	public static void test2() {

		StringBuilder sb = new StringBuilder();
		long start = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			sb.append("jiajun");
		}
		long end=System.currentTimeMillis()-start;
		System.out.println(end);
	}
    //輸出:18 26
}
  • 通過實驗可以看出,適當的初始化容量可以提高性能,因為當不初始化容量的時候,如果此時append超出容量,那么將會從新創建一個char數組,並且進行復制

總結

  • 用new創建對象的時候,會在堆中創建對象,而如果是直接用引號形式的話,會先看常量池是否有此字符串,有的話指向常量池的字符串
  • StringBuilder是非線程安全的,StringBuffer是線程安全的
  • 使用StringBuilder和StringBuffer的時候最好初始化一個合適的容量,因為如果默認容量不夠的話,會重新創建一個char數組,再進行復制
  • 字符串相加的時候,直接相加的時候,編譯器會進行優化,而如果是間接相加的時候,實際上會創建一個StringBuilder來進行append

我覺得分享是一種精神,分享是我的樂趣所在,不是說我覺得我講得一定是對的,我講得可能很多是不對的,但是我希望我講的東西是我人生的體驗和思考,是給很多人反思,也許給你一秒鍾、半秒鍾,哪怕說一句話有點道理,引發自己內心的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)

作者:jiajun 出處: http://www.cnblogs.com/-new/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】,希望能夠持續的為大家帶來好的技術文章!想跟我一起進步么?那就【關注】我吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM