在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 這兩個值不同了。
如下圖所示:
要解釋這個問題,下面是最關鍵部分的代碼:
- // 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()方法真實地在堆內存中創建了另一個字符數組.
- // 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);
- }
參考: