在JDK6和JDK7中substring(int beginIndex, int endIndex)这个方法是有区别的。知道它的区别有助于你用好它。最简单的时候,我们会用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);

输出是

bc

2.当substring()调用的时候发生了什么?

你可能知道字符串x是不变的。当x被赋予x.substring(1,3)结果的时候,它的指针通常是指向了一个新的字符串。如下图:

string 不可改变性

然而这图表示并不完全对,或者它只是表示了在堆里真实的情况。在 JDK 6 和 JDK 7中,调用substring()方法实际处理是不同的。

3.在JDK 6中的substring()

字符串是由一个char的数组表示。在JDK 6中,String类有3个字段:char value[], int offset, int count。它们是用来储存实际的字符阵列(char value[]),该阵列的第一个索引(int offset),该字符串中的字符数(int count)。

当substring()被调用的时候,它创建了一个新的字符串,但是这个字符串的值还是指向的相同数组中的堆。两个字符串不同的地方的地方只是count值和offset值。

string 不可改变性

下面的代码可以简单解释这个问题。

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.在DK 6中substring()带来的问题

如果你有一个非常非常长的的字符串,但是当你使用substring()的时候,你仅需要一小部分。这将导致性能问题,当你只需要一小部分的时候,你还是保存了全部的值。对于DK 6来说通常使用下面的方法解决,它将指针指向实际的子串:

1
x = x.substring(x, y) + ""

5.在DK 7中使用Substring()

在JDK 7中改善了上述问题。在JDK 7中,substring()会创建一个新的数组在堆里。

string 不可改变性

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);
}

延伸阅读

如果你对Java String感兴趣还可以读一下下面的内容

参考文档