JDK6和JDK7中String的substring()方法及其差異
阿新 • • 發佈:2019-02-01
翻譯人員: 鐵錨
翻譯日期: 2013年11月2日
原文連結: The substring() Method in JDK 6 and JDK 7
在JDK6與JDK7這兩個版本中,substring(int beginIndex, int endIndex)方法是不同的. 瞭解兩個版本間的區別可以讓你更好地使用它們. 為簡單起見,本文中以 substring() 表示 substring(int beginIndex, int endIndex).
1. substring()功能簡介
String物件的substring(int beginIndex, int endIndex)方法返回此物件的一個子串,從beginIndex 開始,一直到 endIndex-1 結束,共 (endIndex - beginIndex)個字元。
新手提示:
1.1 String 的索引和陣列一樣,都是從0開始.
1.2 注意,方法名字是substring(),全小寫.
1.3 有個過載方法是substring(int beginIndex),從beginIndex索引處開始,取得子字串.
執行結果(包含索引為 begin,直到 end-1 的字元):
2. 當substring()被呼叫時,發生了什麼?
你應該知道,因為 x 是不可變的,當 指定 x 等於 x.substring(begin, end)時,實際上 x 指向了一個全新的字串,如下圖所示:
然而,這幅圖並不是完全正確的,堆記憶體中所真正發生的事也不是這麼簡單.那麼,在JDK6和JDK7之間 substring()的呼叫到底有些什麼區別呢?
3. JDK 6中的substring()方法
String實際上是一個字元陣列.在 JDK6中, String物件主要包含3個屬性域:
他們用於儲存實際的字元陣列,陣列的第一個索引,以及String的字元個數.
當呼叫 substring() 方法時,建立了一個新的String物件,但是string的value[] 屬性域仍然指向堆記憶體中的原來的那個陣列。區別就是 兩個物件的 count 和 offset 這兩個值不同了。 如下圖所示:
圖2 要解釋這個問題,下面是最關鍵部分的程式碼:
4. JDK 6中substring()引起的問題
如果有一個"非常"長的字串,但每次使用substring()時只想要很小的一部分,那麼將會引起另一個性能問題: 雖然你只需要很小的一部分,但是持有了整個value[]的引用,從而導致大量記憶體被佔用。
要解決這個問題,在JDK6中可以讓其指向一個真正的子字串,示例程式碼:
5. JDK 7中的substring()方法
在JDK 7 中這個問題得到改進, substring()方法真實地在堆記憶體中建立了另一個字元陣列.
圖3
參考:
2. Java 6 vs Java 7 when implementation matters
相關閱讀:
翻譯日期: 2013年11月2日
原文連結: The substring() Method in JDK 6 and JDK 7
在JDK6與JDK7這兩個版本中,substring(int beginIndex, int endIndex)方法是不同的. 瞭解兩個版本間的區別可以讓你更好地使用它們. 為簡單起見,本文中以 substring() 表示 substring(int beginIndex, int endIndex).
1. substring()功能簡介
String物件的substring(int beginIndex, int endIndex)方法返回此物件的一個子串,從beginIndex 開始,一直到 endIndex-1 結束,共 (endIndex - beginIndex)個字元。
新手提示:
1.1 String 的索引和陣列一樣,都是從0開始.
1.2 注意,方法名字是substring(),全小寫.
1.3 有個過載方法是substring(int beginIndex),從beginIndex索引處開始,取得子字串.
String x = "abcdef";
int begin=1;
int end=3;
x = x.substring(begin, end);
System.out.println(x);
執行結果(包含索引為 begin,直到 end-1 的字元):
bc
2. 當substring()被呼叫時,發生了什麼?
你應該知道,因為 x 是不可變的,當 指定 x 等於 x.substring(begin, end)時,實際上 x 指向了一個全新的字串,如下圖所示:
圖1
然而,這幅圖並不是完全正確的,堆記憶體中所真正發生的事也不是這麼簡單.那麼,在JDK6和JDK7之間 substring()的呼叫到底有些什麼區別呢?
3. JDK 6中的substring()方法
String實際上是一個字元陣列.在 JDK6中, String物件主要包含3個屬性域:
private final char value[];
private final int offset;
private final int count;
他們用於儲存實際的字元陣列,陣列的第一個索引,以及String的字元個數.
當呼叫 substring() 方法時,建立了一個新的String物件,但是string的value[] 屬性域仍然指向堆記憶體中的原來的那個陣列。區別就是 兩個物件的 count 和 offset 這兩個值不同了。 如下圖所示:
圖2 要解釋這個問題,下面是最關鍵部分的程式碼:
// JDK6,包級私有構造,共享 value陣列提升速度
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
public String substring(int beginIndex, int endIndex) {
// ... 檢查邊界的程式碼
// 如果範圍和自己一模一樣,則返回自身,否則用value字元陣列構造一個新的物件
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
4. JDK 6中substring()引起的問題
如果有一個"非常"長的字串,但每次使用substring()時只想要很小的一部分,那麼將會引起另一個性能問題: 雖然你只需要很小的一部分,但是持有了整個value[]的引用,從而導致大量記憶體被佔用。
要解決這個問題,在JDK6中可以讓其指向一個真正的子字串,示例程式碼:
x = x.substring(begin, end) + "";
5. JDK 7中的substring()方法
在JDK 7 中這個問題得到改進, substring()方法真實地在堆記憶體中建立了另一個字元陣列.
圖3
// JDK 7, 許可權變為 public
public String(char value[], int offset, int count) {
// ... 檢查邊界..
// value 陣列拷貝
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
public String substring(int beginIndex, int endIndex) {
// ... 檢查邊界..
int subLen = endIndex - beginIndex;
// 如果和自身一樣,那就返回自身,否則返回構造的新物件
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
參考:
2. Java 6 vs Java 7 when implementation matters
相關閱讀: