9

我一直在想,如果我这样做String s = "Hello World".substring(0, 5)了,那么我就会得到一个新字符串s = "Hello"。这也记录在 Java API 文档中:“返回一个新字符串,它是这个字符串的子字符串”

但是当我看到以下两个链接时,我开始怀疑了。

Java 中表达式“new String(...)”的目的是什么?

被认为无用的字符串构造函数毕竟是有用的

基本上,他们说如果我使用String s = "Hello World".subString(0, 5),我仍然会得到一个包含“Hello World”字符数组的字符串。

为什么?Java真的以这种方式实现子字符串吗?为什么会这样?为什么不只返回一个全新的较短子字符串?

4

6 回答 6

5

反过来,为什么char[]在不需要的时候分配一个新的?这是一个有效的实现,因为String它是不可变的。它在聚合中节省了分配和内存。

于 2012-05-31T08:34:05.540 回答
4

这应该是一种效率措施。即,当您使用子字符串时,您不会创建新的 char 数组,而只是在现有 char 数组上创建一个窗口。

这值得吗?也许。缺点是它会引起一些混乱(例如,参见这个 SO question),而且每个String对象都需要将偏移信息携带到数组中,即使它没有被使用。

编辑:从 Java 7 开始,这种行为现在已经改变。有关更多信息,请参阅链接的答案

于 2012-05-31T08:35:51.683 回答
1

Java真的是这样实现subString的吗

查看代码(JDK 7)(我已简化),是的:

public String substring(int beginIndex, int endIndex) {
    .......
    return new String(offset + beginIndex, endIndex - beginIndex, value);
}

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

为什么会这样?为什么不只返回一个全新的较短子字符串?

该评论似乎暗示速度是原因

于 2012-05-31T08:45:29.407 回答
1

尽管以前确实 a Stringcreated withsubString()具有相同的支持char[](可能是为了节省复制的空间和时间),但自 Java 7 Update 6 以来不再如此,因为这种共享char[]有其内存开销。如果加载(大)字符串,采用小子字符串并丢弃大字符串,则尤其存在这种开销。如果小字符串保留很长时间,这可能会导致大量不需要的内存使用。

无论如何,在当前版本(Java 7 Update 21)中,使用原始字符串的subString()调用构造函数,然后构造函数从 char 数组中复制指定范围:String(char value[], int offset, int count)char[]

public String(char value[], int offset, int count) {
    if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count < 0) {
        throw new StringIndexOutOfBoundsException(count);
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}
于 2013-05-13T18:32:15.510 回答
0

因为 String 无论如何都是不可变的。所以完全创建一个新对象没有多大意义

于 2012-05-31T08:35:04.193 回答
0

请记住,字符串是不可变的,并且它们占用内存,设想如果每个字符串都创建一个新字符串,则对字符串执行多个子字符串操作!相反,只需创建一个新的 String 对象,该对象指向相同的不可变字符串,但具有不同的 offset 和 count 属性。现在,无论您对原始字符串或该字符串的子字符串执行多少子字符串,内存中都只有一个字符串本身的副本。效率更高。

另外,在做的时候要String s = "Hello, World".substring(0,5);考虑操作的顺序。首先将在堆上创建字符串“Hello, World”,并且一个全新的 String 对象将指向它。然后将在新的 String 对象上调用 substring 方法,并创建另一个由s实例指向的新 String 对象。所以,因此,s指向堆“Hello, World”上的字符串,offset并且 a 为 0,acount为 5。

于 2013-05-13T17:56:23.723 回答