我们何时应该在字符串文字上使用String的实习方法

Rak*_*yal 184 java string string-interning

根据String #intern(),intern如果在String池中找到String,则该方法应该从String池返回String,否则将在String池中添加新的字符串对象并返回此String的引用.

所以我试过这个:

String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();

if ( s1 == s2 ){
    System.out.println("s1 and s2 are same");  // 1.
}

if ( s1 == s3 ){
    System.out.println("s1 and s3 are same" );  // 2.
}
Run Code Online (Sandbox Code Playgroud)

我期待s1 and s3 are same将被打印为s3被实习,并且s1 and s2 are same不会被打印.但结果是:两行都打印出来.这意味着,默认情况下,字符串常量被实现.但如果是这样,那么为什么我们需要这种intern方法呢?换句话说,我们什么时候应该使用这种方法?

Fil*_*eca 227

Java自动实习字符串文字.这意味着在许多情况下,==运算符似乎对字符串的工作方式与对int或其他原始值的处理方式相同.

由于对于字符串文字自动实习,因此该intern()方法将用于构造的字符串new String()

使用你的例子:

String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();
String s4 = new String("Rakesh");
String s5 = new String("Rakesh").intern();

if ( s1 == s2 ){
    System.out.println("s1 and s2 are same");  // 1.
}

if ( s1 == s3 ){
    System.out.println("s1 and s3 are same" );  // 2.
}

if ( s1 == s4 ){
    System.out.println("s1 and s4 are same" );  // 3.
}

if ( s1 == s5 ){
    System.out.println("s1 and s5 are same" );  // 4.
}
Run Code Online (Sandbox Code Playgroud)

将返回:

s1 and s2 are same
s1 and s3 are same
s1 and s5 are same
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅JavaTechniques"String Equality and Interning".

  • @Miguel不错的解释,我的问题是如何在你的例子中创建对象.这是我的假设:`String s1 ="Rakesh";`first OB1`String s4 = new String("Rakesh");`Second OB2 So(s2,s3,s5)的其余部分引用在''中创建的同一个对象(OB1) string Pool'所以我可以说`.intern()`方法用于阻止创建新对象,如果`string pool'中有相同的字符串,如果我的假设是错误的那么给我指点. (4认同)

Car*_*icz 20

在最近的一个项目中,一些庞大的数据结构被设置为从数据库读入的数据(因此不是字符串常量/文字),但是有大量的重复.这是一个银行应用程序,像一个适度的集(可能是100或200)公司的名称似乎到处都是.数据结构已经很大,如果所有这些公司名称都是唯一的对象,那么它们就会溢出内存.相反,所有数据结构都引用了相同的100或200个String对象,从而节省了大量空间.

实习字符串的另一个小优点是==可以使用(成功!)比较字符串,如果所有涉及的字符串都保证被实现.除了更精简的语法之外,这也是性能增强.正如其他人所指出的那样,这样做会带来很大的引入编程错误的风险,所以这应该只是作为最后手段的绝对措施.

缺点是,实际生成String需要花费更多时间而不是简单地将其放在堆上,并且可能会限制实习字符串的空间,具体取决于Java实现.当你处理一个已知合理数量的字符串并且有很多重复时,最好这样做.

  • @Rakesh:通常在任何给定的类中都没有那么多的String常量,所以它不是常量的空间/时间问题. (2认同)
  • +1.我认为这是一个很好的例子,说明实习是否有意义.我不同意`==`的字符串. (2认同)
  • 从Java 7开始,“字符串池”被实现到堆空间中,因此它具有存储实习生、垃圾收集的所有优点,并且它的大小不受限制,它可以增加到堆大小。(你永远不需要那么多)字符串的内存) (2认同)

Ale*_*yak 14

我希望在使用==实习字符串时增加2美分.

首先要做的String.equalsthis==object.

因此虽然有一些微不足道的性能提升(你不是在调用方法),但从维护者的角度来看,使用==是一场噩梦,因为一些实习字符串倾向于变成非实习.

所以我建议不要依赖于==实习字符串的特殊情况,但总是equals像Gosling那样使用.

编辑:实习成为非实习:

V1.0
public class MyClass
{
  private String reference_val;

  ...

  private boolean hasReferenceVal ( final String[] strings )
  {
    for ( String s : strings )
    {
      if ( s == reference_val )
      {
        return true;
      }
    }

    return false;
  }

  private void makeCall ( )
  {
     final String[] interned_strings =  { ... init with interned values ... };

     if ( hasReference( interned_strings ) )
     {
        ...
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

在2.0版本中,维护者决定hasReferenceVal公开,而没有详细说明它需要一个内部字符串数组.

V2.0
public class MyClass
{
  private String reference_val;

  ...

  public boolean hasReferenceVal ( final String[] strings )
  {
    for ( String s : strings )
    {
      if ( s == reference_val )
      {
        return true;
      }
    }

    return false;
  }

  private void makeCall ( )
  {
     final String[] interned_strings =  { ... init with interned values ... };

     if ( hasReference( interned_strings ) )
     {
        ...
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在你有一个可能很难找到的bug,因为在大多数情况下,数组包含文字值,有时使用非文字字符串.如果equals使用而不是==那时hasReferenceVal仍然会继续工作.再一次,性能提升微乎其微,但维护成本很高.

  • 好吧,我以为你指的是字符串实际上是从实习池中徘徊并因为JVM中的魔法而进入堆.你所说的是==更有可能使某些类程序员错误. (2认同)
  • intern 不适合直接比较 (==),即使两个字符串都被 interned 时它也能工作。降低总内存使用量是非常好的:当同一个字符串在超过 1 个地方使用时。 (2认同)

Boz*_*zho 12

默认情况下,字符串文字和常量是固定的.也就是说,"foo" == "foo"(由字符串文字声明),但是new String("foo") != new String("foo").

  • 所以,问题是我们什么时候应该使用`实习生', (4认同)

KGh*_*tak 7

学习Java String Intern-一劳永逸

从设计上讲,Java中的字符串是不可变的对象。因此,两个具有相同值的字符串对象默认情况下将是不同的对象。但是,如果我们希望节省内存,则可以通过称为字符串实习生的概念来指示使用相同的内存。

以下规则将帮助您清楚地理解该概念:

  1. 字符串类维护一个最初为空的内部池。该池必须保证只包含唯一值的字符串对象。
  2. 具有相同值的所有字符串文字必须被视为相同的内存位置对象,因为否则它们没有区别的概念。因此,所有具有相同值的此类文字将在内部存储池中创建一个条目,并将引用相同的内存位置。
  3. 两个或更多文字的串联也是文字。(因此,规则2适用于他们)
  4. 创建为对象的每个字符串(即通过文字方法以外的任何其他方法)将具有不同的内存位置,并且不会在内部缓冲池中进行任何输入
  5. 文字与非文字的串联将构成非文字。因此,结果对象将具有新的内存位置,并且不会在内部池中进行输入。
  6. 在字符串对象上调用intern方法,要么创建一个进入内部池的新对象,要么从池中返回具有相同值的现有对象。不在内部缓冲池中的任何对象上的调用都不会将该对象移到池中。而是创建另一个进入池的对象。

例:

String s1=new String (“abc”);
String s2=new String (“abc”);
If (s1==s2)  //would return false  by rule #4
If (“abc” == “a”+”bc” )  //would return true by rules #2 and #3
If (“abc” == s1 )  //would return false  by rules #1,2 and #4
If (“abc” == s1.intern() )  //would return true  by rules #1,2,4 and #6
If ( s1 == s2.intern() )      //wound return false by rules #1,4, and #6
Run Code Online (Sandbox Code Playgroud)

注意:这里不讨论字符串实习生的动机情况。但是,节省内存无疑将是主要目标之一。

  • 谢谢你的#3,我不知道:) (2认同)