本文摘錄自:http://www.importnew.com/7418.html
substring(int beginIndex, int endIndex)在JDK6與JDK7中的實現方式不一樣,理解他們的差異有助於更好的使用它們。爲了簡單起見,下面所說的substring()指的就是substring(int beginIndex, int endIndex)方法。
1.substring()是做什麼的?
substring(int beginIndex ,int endIndex)方法返回一個子字符串,返回的是從原字符串的beginIndex到endIndex-1之間的內容。
1
2
3
|
String
x = "abcdef" ; x
= x.substring( 1 , 3 ); System.out.println(x); |
輸出:
1
|
"bc" |
2.當substring()被調用的時候,內部發生什麼事?
你或許會認爲由於x是不可變的對象,當x被x.substring(1,3)返回的結果賦值後,它將指向一個全新的字符串如下圖:
然而,這個圖並不完全正確,或者說並沒有完全表示出java 堆中真正發生的事情。那麼當調用substring()的時候到底發生的了什麼事呢?JDK 6與JDK7的substring方法實現有什麼不一樣呢?
3.JDK6中的substring()
java中字符串是通過字符數組來支持實現的,在JDK6中,String類包含3個域,char[] value、int offset、int count。分別用於存儲真實的字符數組、數組的偏移量,以及String所包含的字符的個數。
當substring()方法被調用的時候,它會創建一個新的字符串對象,但是這個字符串的值在java 堆中仍然指向的是同一個數組,這兩個字符串的不同在於他們的count和offset的值。
下面是jdk6中的原代碼,是簡化後只包含用來說明這個問題的關鍵代碼:
1
2
3
4
5
6
7
8
9
10
11
|
//JDK
6 String( int offset, int count, char value[])
{ this .value
= value; this .offset
= offset; this .count
= count; } public String
substring( int beginIndex, int endIndex)
{ //check
boundary return new String(offset
+ beginIndex, endIndex - beginIndex, value); } |
4.jdk6中substring()將會導致的問題
如果你有一個非常長的字符串,但是你僅僅只需要這個字符串的一小部分,這就會導致性能問題(譯註:可能會造成內存泄露,這個bug很早以前就有提及),因爲你需要的只是很小的部分,而這個子字符串卻要包含整個字符數組,在jdk6中解決辦法就是使用下面的方法,它會指向一個真正的子字符串。
1
|
x
= x.substring(x, y) + "" |
5.JDK7中的substring()
在JDK7中有所改進,substring()方法在堆中真正的創建了一個新的數組,當原字符數組沒有被引用後就被GC回收了.因此避免了上述問題.
1
2
3
4
5
6
7
8
9
10
11
|
//JDK
7 public String( char value[], int offset, int count)
{ //check
boundary this .value
= Arrays.copyOfRange(value, offset, offset + count); } public String
substring( int beginIndex, int endIndex)
{ //check
boundary int subLen
= endIndex - beginIndex; return new String(value,
beginIndex, subLen); } |
譯註: 在最新的Oralce JDK 6u45中,substring的實現方式已經改進了,不過我查到openjdk的6-b14就是原來的實現方式,有興趣的可以查查具體是哪個版改進的.