Java 7string – 子string的复杂性

在Java 6之前,我们在String上有一个恒定的时间子String 。 在Java 7中,为什么他们决定去复制char数组,并降低到线性时间复杂度 – 当像StringBuilder这样的东西正是为了这个?

为什么他们决定在Oracle错误#4513622中讨论:(str)保留一个字段的子string可以防止对象的GC :

当您像在示例中一样调用String.substring时,不会分配用于存储的新字符数组。 它使用原始string的字符数组。 因此,支持原始string的字符数组不能被GC'd,直到子string的引用也可以GC'd。 这是一个有意的优化,以防止在常见场景中使用子string时出现过多的分配。 不幸的是,有问题的代码遇到了原始数组的开销显而易见的情况。 对于两个边缘情况来说,都很难优化。 任何对空间/尺寸权衡的优化通常都很复杂,通常可能是平台特定的。

还有这个说明 ,指出曾经是优化的东西根据testing已经变成了一个悲观的事情:

很长一段时间,准备工作和刨削工作已经从java.lang.String中移除了offset和count字段。 这两个字段使多个String实例共享相同的支持字符缓冲区。 共享字符缓冲区是旧基准的一个重要优化,但是使用当前真实世界的代码和基准,实际上最好不要共享后备缓冲区。 共享字符数组备份缓冲区只有“赢”与非常繁重的使用String.substring。 受到负面影响的情况可能包括parsing器和编译器,但是目前的testing表明,总体而言这种改变是有益的。

如果您有一个短命的大型父string的长寿命小型子string,则支持该父string的大型char []将不符合垃圾回收的条件,直到小子string移出作用域。 这意味着一个子串可以占用比人们所期望的更多的内存。

Java 6方式执行的唯一好处是,当某人从一个大的父string中取出一个很大的子string时,这是非常罕见的情况。

显然,他们认为这种变化的微小的性能成本已经被旧方式隐藏的内存问题所压倒。 决定因素是问题隐藏了,而不是有一个解决方法。

这将影响像后缀数据这样的数据结构的复杂性。 Java应该提供一些替代方法来获取原始string的一部分。

这只是他们修复一些JVM垃圾收集限制的蹩脚方式。

在Java 7之前,如果我们想要避免垃圾回收不起作用的问题,我们总是可以复制子string,而不是保留subString引用。 这只是对复制构造函数的额外调用:

 String smallStr = new String(largeStr.substring(0,2)); 

但是现在,我们不能再有一个固定的时间subString。 这悲剧。

我相信主要动机是String和它的char[]的最终“共同定位”。 现在他们在远处定位,这是高速caching线上的一个重大惩罚。 如果每个String拥有它的char[] ,那么JVM可以将它们合并在一起,读取速度会更快。