首页 文章

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

提问于
浏览
168

根据String#intern(),如果在String池中找到String,则 intern 方法应该从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.
}

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

15 回答

  • 2

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

  • 10
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String s1 = "test";
        String s2 = new String("test");
        System.out.println(s1==s2);              //false
        System.out.println(s1==s2.intern());    //true --> because this time compiler is checking from string constant pool.
    }
    
  • 1
    String p1 = "example";
    String p2 = "example";
    String p3 = "example".intern();
    String p4 = p2.intern();
    String p5 = new String(p3);
    String p6 = new String("example");
    String p7 = p6.intern();
    
    if (p1 == p2)
        System.out.println("p1 and p2 are the same");
    if (p1 == p3)
        System.out.println("p1 and p3 are the same");
    if (p1 == p4)
        System.out.println("p1 and p4 are the same");
    if (p1 == p5)
        System.out.println("p1 and p5 are the same");
    if (p1 == p6)
        System.out.println("p1 and p6 are the same");
    if (p1 == p6.intern())
        System.out.println("p1 and p6 are the same when intern is used");
    if (p1 == p7)
        System.out.println("p1 and p7 are the same");
    

    当两个字符串独立创建时, intern() 允许您比较它们,如果之前不存在引用,它还可以帮助您在字符串池中创建引用 .

    当您使用 String s = new String(hi) 时,java会创建一个新的字符串实例,但是当您使用 String s = "hi" 时,java会检查代码中是否存在单词"hi"的实例,如果它存在,则只返回引用 .

    由于比较字符串是基于引用, intern() 有助于您创建引用并允许您比较字符串的内容 .

    在代码中使用 intern() 时,它会清除引用同一对象的字符串所使用的空间,并仅返回内存中已存在的相同对象的引用 .

    但是在使用p5的情况下:

    String p5 = new String(p3);
    

    仅复制p3的内容并新创建p5 . 所以它不是实习生 .

    所以输出将是:

    p1 and p2 are the same
    p1 and p3 are the same
    p1 and p4 are the same
    p1 and p6 are the same when intern is used
    p1 and p7 are the same
    
  • 16

    为什么不能在需要使用实习生的地方使用字符串文字?默认情况下,字符串文字用法将重用现有的字符串文字 . 那么为什么我们需要创建新的String(“something”.intern()而不是仅仅分配“某些东西”?

  • 215
    String s1 = "Anish";
            String s2 = "Anish";
    
            String s3 = new String("Anish");
    
            /*
             * When the intern method is invoked, if the pool already contains a
             * string equal to this String object as determined by the
             * method, then the string from the pool is
             * returned. Otherwise, this String object is added to the
             * pool and a reference to this String object is returned.
             */
            String s4 = new String("Anish").intern();
            if (s1 == s2) {
                System.out.println("s1 and s2 are same");
            }
    
            if (s1 == s3) {
                System.out.println("s1 and s3 are same");
            }
    
            if (s1 == s4) {
                System.out.println("s1 and s4 are same");
            }
    

    OUTPUT

    s1 and s2 are same
    s1 and s4 are same
    
  • 13

    你应该计算两个周期时间,即编译时间和运行时间 . 例如:

    //example 1 
    "test" == "test" // --> true 
    "test" == "te" + "st" // --> true
    
    //example 2 
    "test" == "!test".substring(1) // --> false
    "test" == "!test".substring(1).intern() // --> true
    

    一方面,在示例1中,我们发现结果都返回true,因为在编译时,jvm会将“test”放到文字字符串池中,如果jvm找到“test”存在,那么它将使用exists one,在例1中,“test”字符串都指向相同的内存地址,因此示例1将返回true . 另一方面,在例2中,substring()的方法在运行时执行,在“test”==“!test”.substring(1)的情况下,池将创建两个字符串对象,“测试“and”!test“,所以它们是不同的引用对象,所以这种情况将返回false,在”test“==”!test“.substring(1).intern()的情况下,实习生的方法( )将把“”!test“.substring(1)”放到文字字符串池中,所以在这种情况下,它们是相同的引用对象,因此将返回true .

  • 0

    string intern()方法用于在字符串常量池中创建堆字符串对象的精确副本 . 字符串常量池中的字符串对象会自动实现,但堆中的字符串对象则不会 . 创建实习生的主要用途是节省内存空间并更快地比较字符串对象 .

    资料来源:What is string intern in java?

  • 2

    学习Java String实习生 - 一劳永逸

    java中的字符串是设计中的不可变对象 . 因此,默认情况下,两个具有相同值的字符串对象将是不同的对象 . 但是,如果我们希望节省内存,我们可以通过一个名为string intern的概念来指示使用相同的内存 .

    The below rules would help you understand the concept in clear terms:

    • String类维护一个最初为空的intern-pool . 此池必须保证包含仅具有唯一值的字符串对象 .

    • 具有相同值的所有字符串文字必须被视为相同的内存位置对象,因为它们没有区别的概念 . 因此,具有相同值的所有此类文字将在内部池中生成单个条目,并将引用相同的内存位置 .

    • 两个或多个文字的连接也是文字 . (因此规则#2将适用于他们)

    • 作为对象创建的每个字符串(即除了文字之外的任何其他方法)将具有不同的内存位置,并且不会在内部池中创建任何条目

    • 文字与非文字的连接将使非文字 . 因此,结果对象将具有新的内存位置,并且不会在内部池中创建条目 .

    • 在字符串对象上调用intern方法,或者创建一个进入内部池的新对象,或者从池中返回具有相同值的现有对象 . 对不在intern-pool中的任何对象的调用不会将对象移动到池中 . 它创建了另一个进入池的对象 .

    Example:

    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
    

    注意:此处不讨论字符串实习生的动机案例 . 但是,节省内存肯定是其中之一主要目标 .

  • 0

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

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

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

  • 3

    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.
    }
    

    将返回:

    s1 and s2 are same
    s1 and s3 are same
    s1 and s5 are same
    

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

  • 0

    Interned Strings避免重复字符串 . 实习以节省更多CPU时间来节省RAM,以检测和替换重复的字符串 . 每个字符串只有一个被复制的副本,无论有多少引用都指向它 .

    虽然Java默认会自动插入所有字符串,但请记住,当它们不是常量时我们只需要实习字符串,并且我们希望能够快速将它们与其他字符串进行比较 . 应该对使用new String()构造的字符串使用intern()方法,以便通过==运算符进行比较 .

    公共类TestString {

    public static void main(String[] args) {
        String s1 = "Test";
        String s2 = "Test";
        String s3 = new String("Test");
        final String s4 = s3.intern();
        System.out.println(s1 == s2);
        System.out.println(s2 == s3);
        System.out.println(s3 == s4);
        System.out.println(s1 == s3);
        System.out.println(s1 == s4);
        System.out.println(s1.equals(s2));
        System.out.println(s2.equals(s3));
        System.out.println(s3.equals(s4));
        System.out.println(s1.equals(s4));
        System.out.println(s1.equals(s3));
    }
    

    }

    //输出

    真正

    真正

    真正

    真正

    真正

    真正

    真正

  • 1

    http://en.wikipedia.org/wiki/String_interning

    string interning是一种只存储每个不同字符串值的一个副本的方法,该值必须是不可变的 . 实习字符串使得一些字符串处理任务更加节省时间或空间,代价是在创建或实现字符串时需要更多时间 . 不同的值存储在字符串实习池中 .

  • 1

    我希望使用带有实习字符串的 == 加2美分 .

    String.equals 做的第一件事是 this==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 ) )
         {
            ...
         }
      }
    }
    

    在版本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 ) )
         {
            ...
         }
      }
    }
    

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

  • 1

    如你所说,字符串 intern() 方法将首先从字符串池中找到,如果找到,则它将返回指向该字符串的对象,或者将新的字符串添加到池中 .

    String s1 = "Hello";
        String s2 = "Hello";
        String s3 = "Hello".intern();
        String s4 = new String("Hello");
    
        System.out.println(s1 == s2);//true
        System.out.println(s1 == s3);//true
        System.out.println(s1 == s4.intern());//true
    

    s1s2 是指向字符串池"Hello"的两个对象,使用 "Hello".intern() 将找到 s1s2 . 所以 "s1 == s3" 返回true,以及 s3.intern() .

  • 1

    Interned Strings避免重复字符串 . 实习以节省更多CPU时间来节省RAM,以检测和替换重复的字符串 . 每个字符串只有一个被复制的副本,无论有多少引用都指向它 . 由于字符串是不可变的,如果两种不同的方法偶然使用相同的字符串,它们可以共享相同字符串的副本 . 将重复的字符串转换为共享字符串的过程称为interning.String.intern(),它为您提供规范主字符串的地址 . 您可以将实际的字符串与简单的==(比较指针)进行比较,而不是比较字符串逐个比较字符串的等号 . 因为字符串是不可变的,所以实习生进程可以自由地进一步节省空间,例如,当没有为"pot"创建单独的字符串文字时它作为"hippopotamus"等其他文字的子字符串存在 .

    要查看更多http://mindprod.com/jgloss/interned.html

相关问题