为什么要进行字符串去重,当我们已经有了字符串池?

9

字符串去重:

在任何应用程序中,字符串消耗了很多内存。每当垃圾收集器访问字符串对象时,会注意到字符数组。它获取它们的哈希值并将其存储在一个弱引用到数组的旁边。一旦它找到另一个具有相同哈希代码的字符串,它将逐个比较它们的字符。如果它们也匹配,则一个字符串将被修改并指向第二个字符串的字符数组。第一个字符数组不再被引用,因此可以进行垃圾回收。

字符串池:

Java程序使用的所有字符串都存储在此处。如果两个变量初始化为相同的字符串值,则不会在内存中创建两个字符串,而只会存储一个副本,并且两者都将指向同一内存位置。

因此,Java已经通过检查字符串是否存在于字符串池中来避免在堆中创建重复字符串。那么,字符串去重的目的是什么?

如果有以下代码

    String myString_1 = new String("Hello World");
    String myString_2 = new String("Hello World");

即使两个字符串相同,它们在内存中也会创建两次。除此之外,我无法想到任何场景需要字符串去重复。显然,我一定漏掉了什么。那我错过了什么?

提前致谢。

6个回答

6

字符串池仅适用于显式添加到其中的字符串,或在应用程序中用作常量的字符串。它不适用于在应用程序生命周期内动态创建的字符串。然而,字符串去重适用于所有字符串。


2

编译时 vs 运行时

字符串池指的是在编译时已知的字符串常量。

字符串去重可以帮助你在运行时避免重复检索(或构造)相同的字符串,例如从文件、HTTP请求或其他方式中读取。


0

字符串去重在String中内置了额外的间接层

  • 使用字符串池,您只能针对两个相同的字符串返回相同的对象。
  • 字符串去重允许您拥有多个不同的String对象共享相同的内容

这意味着可以消除创建时的去重限制:您的应用程序可以持续创建具有相同内容的新String对象,同时占用极少的额外内存,因为字符串的内容将被共享。此过程可以在完全无关的时间表上完成,例如在后台,而您的应用程序几乎不需要CPU资源。由于String对象的标识不会改变,因此去重可以完全隐藏在您的应用程序中。


0

补充以上答案,对于旧的虚拟机,字符串池不会被垃圾回收(现在已经改变,但不要依赖这一点)。它包含应用程序中用作常量的字符串,因此始终需要。如果您不断将所有字符串放入字符串池中,则可能很快耗尽内存。除此之外,去重是一个相对昂贵的过程,如果您知道只需要短时间内使用该字符串,并且有足够的内存。

出于这些原因,字符串不会自动放入字符串池中。您必须通过调用string.intern()来显式地执行。


0

从文档中:

"初始化一个新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要原始字符串的显式副本,否则使用此构造函数是不必要的,因为字符串是不可变的。"

所以我的感觉是,在通常情况下不需要使用String类中的这个构造函数,就像你上面使用的那样。我猜测这个构造函数仅仅是为了完整性或者如果你不想共享该副本(现在有些不必要,参考这里我所说的),但其他构造函数仍然很有用,比如从字符数组获取一个String对象等。


0
我想不到除了这种情形之外,字符串去重还有什么用处。
还有一种(更常见的)情况是使用 StringBuilder。在 StringBuilder 类的 toString() 方法中,它明显会创建一个新的内存实例:
public final class StringBuilder extends AbstractStringBuilder
                                 implements java.io.Serializable, CharSequence
{
    ...

    @Override
    public String toString() {
       // Create a copy, don't share the array
       return new String(value, 0, count);
    }

    ...

}

对于其线程安全版本StringBuffer同样适用:

public final class StringBuffer extends AbstractStringBuilder
                                implements java.io.Serializable, CharSequence
{
   ...

   @Override
   public synchronized String toString() {
       if (toStringCache == null) {
           toStringCache = Arrays.copyOfRange(value, 0, count);
       }
       return new String(toStringCache, true);
   }

   ...
}

在依赖此功能的应用程序中,字符串去重可能会降低内存使用率。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接