字符串类的subString()函数如何工作

时间:2009-04-01 06:30:21

标签: java string

请参阅以下代码。

String s = "Monday";
if(s.subString(0,3).equals("Mon"){}

String s2 = new String(s.subString(0,3));
String s3 = s.subString(0,3);  

我知道第2行仍将指向“星期一”,并且有一个新的String对象,其偏移量和计数设置为0,3。

第4行将在字符串池中创建一个新的字符串“Mon”并指向它。

但不确定第5行是否会表现得像第2行或第4行。

如果第2行或第4行我错了,请更正..

7 个答案:

答案 0 :(得分:9)

正如Pete Kirkham所指出的,这是具体的实施。我的答案仅适用于Sun JRE,并且仅在Java 7更新6之前。

正常的substring调用只是创建一个引用与原始字符串相同的字符数组的新字符串。这就是第5行也发生的事情。新的字符串对象引用恰好分配给变量的事实不会改变方法的行为。

为了清楚起见,你说在第2行中,新字符串仍将指向“Monday” - 字符串中的char数组引用将与用于“Monday”的char数组相同。但“星期一”本身就是一个字符串,而不是字符数组。换句话说,通过时间线2完成(并忽略GC),有两个字符串对象,两者都引用相同的char数组。一个计数为6,另一个计数为3;两者都有0的偏移量。

你对使用“字符串池”的第4行错了 - 那里没有汇集。但是,它与其他线路不同。当您调用String(String)构造函数时,新字符串将获取原始字符数据的副本,因此它是完全独立的。如果您只需要一个包含非常大的原始字符串的一小部分的字符串,这将非常有用;当你抓住小部分的副本时,它允许原始的大字符数组被垃圾收集(假设没有别的东西需要它)。根据我自己的经验,这方面的一个很好的例子就是从一条线上读取线条。默认情况下,BufferedLineReader将使用80个字符的缓冲区读取行,因此返回的每个字符串将使用至少80个字符的char数组。如果您正在阅读许多非常短的行(单个单词),那么仅通过使用奇怪的内容来消耗内存的差异

line = new String(line);

非常重要。

这有帮助吗?

答案 1 :(得分:7)

  

我知道第2行仍将指向“星期一”,并且有一个新的String对象,其偏移量和计数设置为0,3。

目前Sun JRE的实施情况也是如此。我似乎记得过去的Sun实现并不是这样,并且JVM的其他实现也不是这样。不要依赖未指定的行为。 GNU类路径可能会复制数组(我不记得手头用什么比率来决定何时复制,但是如果副本是原始的一小部分就会复制,这会将一个很好的O(N)算法转换为O(N ^ 2))。

  

第4行将在字符串池中创建一个新的字符串“Mon”并指向它。

不,它在堆中创建一个新的字符串对象,受到与任何其他对象相同的垃圾收集规则的约束。它是否共享相同的底层字符数组是依赖于实现的。不要依赖未指定的行为。

String(String)构造函数说:

  

初始化一个新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。

String(char[])构造函数说:

  

分配一个新的String,使其表示当前包含在字符数组参数中的字符序列。复制字符数组的内容;后续修改字符数组不会影响新创建的字符串。

遵循良好的OO原则,String的任何方法实际上都不需要使用字符数组来实现,因此String的规范的任何部分都不需要对字符数组进行操作。将数组作为输入的那些操作指定将数组的内容复制到String中使用的任何内部存储。字符串可以在内部使用UTF-8或LZ压缩,并符合API。

但是,如果您的JVM没有进行小比例子字符串优化,那么当您使用new String(String)时,它有可能只复制相关部分,所以这是一个尝试它的情况看它是否改善了内存使用。并非所有影响Java运行时的东西都是由Java定义的。

要获取字符串池中equal到字符串的字符串,请使用intern()方法。这将从池中检索字符串(如果已经实现了该值的字符串),或者创建新字符串并将其放入池中。请注意,池化的字符串具有不同的(再次依赖于实现的)垃圾收集行为。

答案 2 :(得分:3)

注意:从Sun / Oracle Java中的Java 7更新6开始,String.substring创建的String不再共享父级的char数组。我们认为这种优化很少有用,并且不能证明offsetcount字段的成本和复杂性。

一些链接:

答案 3 :(得分:1)

第5行----> s3 =周一。

答案 4 :(得分:0)

在Sun的实现中,String对象具有private final char value[]字段。通过调用substring()创建新String时,不会创建新的char数组,新实例将使用原始对象的value。第2行和第5行就是这种情况,新的String对象将使用s的char数组。

如果字符串长度小于char数组value的总长度,则构造函数String(String)会创建一个新的char数组。因此,第4行中创建的String将使用新的char数组。

你应该看一下构造函数public String(String original)的source code,它非常简单。

答案 5 :(得分:0)

“子字符串通过从原始字符串中提取一部分原始字符串来创建新对象”。

直到Java 1.7,子字符串都保留了原始字符数组的引用,这意味着即使是5个字符长的子字符串,也可以通过保留强引用来防止1GB字符数组被垃圾回收。

此问题已在Java 1.7中修复,在Java 1.7中不再引用原始字符数组,但是这种更改也使创建子字符串位的时间花费很大。先前它在O(1)的范围内,在Java 7的最坏情况下可能是O(n)。

enter image description here

答案 6 :(得分:-1)

阅读此http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html

  

“返回一个新字符串......”