什么时候我们应该在字符串文字上使用字符串的Intern方法?

根据字符串#intern(),如果在String Pool中找到字符串,则intern方法应该返回String Pool中的字符串,否则将在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.但如果是这样的话,为什么我们需要intern方法呢?换句话说,我们应该在什么时候使用这种方法?

117037 次浏览
默认情况下,

字符串文字和常量是暂留的。 即,"foo" == "foo"(由字符串文字声明),但new String("foo") != new String("foo")

Java自动插入字符串文字。这意味着在许多情况下,==运算符对字符串的作用方式与对整数或其他原始值的作用方式相同。

由于字符串文字的插入是自动的,因此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

在除s4变量之外的所有情况下,其值是使用new运算符显式创建的,并且intern方法未在其结果上使用,它是在ABC_3_返回的单个不可变实例。

更多信息,请参阅JavaTechniques “字符串相等和交互”

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

嵌套字符串的另一个小优点是,如果保证所有涉及的字符串都被嵌套,那么==可以用来(成功地!)比较字符串。除了更精简的语法之外,这也是一种性能增强。正如其他人所指出的,但是,这样做会带来引入编程错误的巨大风险,因此只应将其作为最后的单独措施。

缺点是插入字符串比简单地将其放在堆上要花费更多的时间,并且插入字符串的空间可能有限,这取决于Java实现。当您处理具有许多重复的已知合理数量的字符串时,最好这样做。

我想在使用==和Interned字符串上添加我的两分钱。

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

现在你有一个bug,可能很难发现,因为在大多数情况下,数组包含文字值,有时使用非文字字符串。如果使用equals代替==,则hasReferenceVal将仍然继续工作。同样,性能提升微乎其微,但维护成本却很高。

插入的字符串避免了重复的字符串。Interning以更多的CPU时间来检测和替换重复的字符串为代价来节省RAM.无论有多少个引用指向它,每个已插入的字符串都只有一个副本。由于字符串是不可变的,如果两个不同的方法偶然使用相同的字符串,它们可以共享相同字符串的副本。将复制字符串转换为共享字符串的过程称为Interning.String.Intern()给出了规范主字符串的地址。您可以使用简单的==(比较指针)来比较内部字符串,而不是使用等于(逐个比较字符串的字符)。因为字符串是不可变的,所以Intern进程可以自由地进一步节省空间,例如,当“ POT ”作为其他文字(如“ Hippopotamus ”)的子字符串存在时,不为其创建单独的字符串文字。

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

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

String Interning是一种仅存储每个不同字符串值的一个副本的方法,该副本必须是不可变的。插入字符串使某些字符串处理任务的时间或空间效率更高,但代价是在创建或插入字符串时需要更多时间。不同的值存储在String Intern池中。

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");
}

输出

s1 and s2 are same
s1 and s4 are same

你应该确定两个时间段,即编译时间和运行时间。例如:

//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 ”存在,那么它将使用存在的字符串,在示例1中,“ test ”字符串都指向相同的内存地址,因此示例1将返回true. 另一方面,在示例2中,方法substring()在运行时执行, 在“ test ”==“!test ”.substring(1)的情况下,池将创建两个字符串对象“ test ” 和“!test ”,因此它们是不同的引用对象,所以在这种情况下将返回false,在“ test ”==“!test ”.substring(1).intern()的情况下,intern()的方法将把“ ”!tests “.substring(1)”放到文字字符串池中,因此在这种情况下,它们是相同的引用对象,所以将返回true.

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

来源:_农行_0

正如您所说的,字符串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"返回真,s3.intern()也返回真。

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

学习Java字符串实习生-一劳永逸

Java中的字符串在设计上是不可变的对象。因此,两个字符串对象即使具有相同的值,默认情况下也是不同的对象。然而,如果我们希望节省内存,我们可以通过一个名为String Intern

的概念来指示使用相同的内存。以下规则将帮助您清楚地理解这一概念:

  1. String类维护一个初始为空的Intern-Pool.该池必须保证包含仅具有唯一值的字符串对象。
  2. 所有具有相同值的字符串文字必须被视为相同的内存位置对象,因为它们没有区别的概念。因此,具有相同值的所有此类文字将在Intern-Pool中创建单个条目,并将引用相同的内存位置。
  3. 两个或多个文字的连接也是文字。(因此,规则#2将适用于他们)
  4. 作为对象创建的每个字符串(即通过除文字以外的任何其他方法)将具有不同的内存位置,并且不会在Intern-Pool中创建任何条目
  5. 文字与非文字的连接将生成非文字。因此,结果对象将具有新的内存位置,并且不会在Intern-Pool中创建条目。
  6. 在String对象上调用Intern方法,要么创建一个进入Intern-Pool的新对象,要么从池中返回一个具有相同值的现有对象。对不在Intern-Pool中的任何对象的调用不会将该对象移动到池中。而是创建另一个进入池的对象。

示例:

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

注意:这里不讨论字符串实习生的激励案例。然而,节省内存肯定是主要目标之一。

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

通过使用堆对象引用,如果我们想要获得相应的字符串常量池对象引用,那么我们应该去实习生()

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


System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true

图片视图 enter image description here

步骤1: 在堆和字符串常量池中创建具有数据“ rakesh ”的对象。此外,S1始终指向堆对象。

步骤2: 通过使用堆对象引用S1,我们尝试使用intern()

获取相应的字符串常量池对象引用S2。

第三步: 故意在字符串常量池中创建一个包含数据“ rakesh ”的对象,由名称S3

引用

“==”运算符表示引用比较。

正在获取S1==S2的错误

正在获取S2==S3的真的

希望这对你有帮助!