文字列リテラルで文字列のインターンメソッドを使用する必要がある場合


187

String#intern()によると、internメソッドは、文字列が文字列プールにある場合、文字列プールから文字列を返すことになっています。そうでない場合、新しい文字列オブジェクトが文字列プールに追加され、この文字列の参照が返されます。

だから私はこれを試しました:

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 sames3がインターンされたときに印刷され、s1 and s2 are same印刷されないことを期待していました。しかし、結果は次のとおりです。両方の行が印刷されます。つまり、デフォルトでは文字列定数がインターンされます。それがそうであるなら、なぜ我々は必要なのですintern方法を?つまり、このメソッドをいつ使用すればよいのでしょうか。


14
リンクしたJavadocにも、「すべてのリテラル文字列と文字列値の定数式がインターンされている」と記載されています。
ジョーン


1
正確な複製ではない..
Bozho

1
@ジョン:そうです。では、なぜinternパブリックメソッドを使用するのでしょうか。internプライベートメソッドを使用して、誰もアクセスできないようにするべきではありません。または、この方法の目的はありますか?
Rakesh Juyal

2
@RakeshJuyal:インターンメソッドは、文字列リテラルまたは変数である文字列型で定義されます。メソッドがプライベートの場合、変数をどのようにインターンしますか?
bobbyalex 2013

回答:


230

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

s4変数以外のすべてのケースで、new演算子を使用して明示的に作成された値と場所intern、その結果にメソッドが使用されなかった場合、JVMの文字列定数プールが返されるのは単一の不変インスタンスです。

詳細については、JavaTechniquesの「String Equality and Interning」を参照してください。


Javaは、最適化のために文字列リテラルを自動的にインターンすることを想定しています。これは、文字列が不変であるという理由でのみ安全に実行できますか?
スタイル

Javaの初心者(私はC#.NETの出身です)で、Javaのレガシープロジェクト "" .intern()で時々見られるので、正しく理解すれば、これは空の文字列に対しても "ナンセンス"であることがわかります。
hfrmobile 2013

4
@ミゲル素敵な説明、私の質問は、あなたの例でここにオブジェクトがどのように作成されるかです。ここに私の仮定があります: String s1 = "Rakesh"; 最初のOB1 String s4 = new String("Rakesh");私はそれを言うことができる「という文字列プール」そうで作成し(S2、S3、S5)参照同じオブジェクト(OB1)の第二OB2だから、休み.intern()を防ぐために使用される方法は、新しいオブジェクトを作成する場合で利用可能な同じ文字列string poolの場合私の仮定は間違っているので、方向を教えてください。
HybrisHelp 2013

1
JavaTechniquesリンクが壊れている
SJuan76


20

最近のプロジェクトでは、いくつかの巨大なデータ構造がデータベースから読み込まれたデータ(したがって、文字列定数/リテラル​​ではない)で設定されましたが、大量の重複がありました。それは銀行のアプリケーションであり、控えめなセット(おそらく100または200)の企業の名前のようなものが至る所に現れました。データ構造はすでに大規模であり、それらすべての企業名が一意のオブジェクトであった場合、メモリがオーバーフローしていたでしょう。代わりに、すべてのデータ構造が同じ100または200のStringオブジェクトへの参照を持っているため、多くのスペースを節約できます。

インターンされたストリングのもう1つの小さな利点は、==関連するすべてのストリングがインターンされることが保証されている場合にストリングを比較するために(正常に)使用できることです。よりスリムな構文とは別に、これはパフォーマンスの向上でもあります。しかし、他の人が指摘しているように、これを行うとプログラミングエラーが発生する大きなリスクがあるため、これは最後の手段の切実な手段としてのみ行う必要があります。

欠点は、Stringのインターンは単にヒープにスローするよりも時間がかかることと、Java実装によっては、インターンされたStringのスペースが制限される可能性があることです。多くの重複がある既知の妥当な数の文字列を処理している場合に最適です。


@ The downside is that interning a String takes more time than simply throwing it on the heap, and that the space for interned Strings may be limitedString定数にインターンメソッドを使用しなくても、自動的にインターンされます。
Rakesh Juyal 2009

2
@Rakesh:通常、特定のクラスにはそれほど多くの文字列定数はありません。そのため、定数の場合の空間/時間の問題ではありません。
デビッドロドリゲス-ドリベス09

はい、Rakeshのコメントは当てはまりません。なぜなら、内部ストリングは(明示的に)何らかの方法で「生成」されたストリングでのみ行われるためです。内部操作によるものか、データベースなどからの取得によるものです。定数の場合、選択肢はありません。
Carl Smotricz、2009

2
+1。これはインターンが理にかなっている良い例だと思います。==文字列については同意しません。
Alexander Pogrebnyak

1
Java 7以降、「文字列プール」はヒープ領域に実装されるため、インターン、ガベージコレクション、およびそのサイズを制限することなく、ヒープサイズまで増やすことができます。文字列のメモリ)
Anil Uttani 2017

15

==インターンされた文字列での使用に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まだ持って仕事に続けるだろう。この場合も、パフォーマンスの向上はわずかですが、メンテナンスコストが高くなります。


「一部の抑留された文字列は抑留されなくなる傾向があります。」うわー、それは...奇妙なことでしょう。参考文献を引用していただけますか?
Carl Smotricz、2009

2
わかりました。JVMの魔法のおかげで、実際にはインターンプールからヒープに移動しているストリングを参照していると思いました。あなたが言っていることは、==は特定のクラスのプログラマーエラーをより起こりやすくするということです。
Carl Smotricz、2009

「したがって、インターンされた文字列では==の特別なケースに依存しないことをお勧めしますが、常にGoslingが意図したとおりに等号を使用します。」これを述べているゴスリングからの直接の引用またはコメントがありますか?そうだとすれば、なぜ彼はintern()と==を言語に使用することさえしなかったのでしょうか。

1
internは、両方の文字列がインターンされている場合は機能しますが、直接比較(==)には適していません。同じ文字列が複数の場所で使用されている場合、使用される総メモリを減らすのに最適です。
tgkprog 2013

12

文字列リテラルと定数はデフォルトでインターンされます。つまり、"foo" == "foo"(文字列リテラルで宣言されています)ですがnew String("foo") != new String("foo")


4
したがって、問題はいつ使用するかということinternです
Rakesh Juyal

これは、stackoverflow.com /questions/ 1833581/when- to -use-intern、および他のいくつかの質問、いくつかは昨日からの質問を指摘していました。
Bozho

このステートメントに対する私の理解String literals and constants are interned by defaultが正しいかどうか教えてください:、 new String("foo")->ここでは、文字列リテラル「foo」が文字列プールに1つとヒープに1つ作成されるため、合計2つのオブジェクトが作成されます。
dkb

8

Java String Internを学びましょう

Javaの文字列は、設計上不変のオブジェクトです。したがって、同じ値の2つの文字列オブジェクトは、デフォルトでは異なるオブジェクトになります。ただし、メモリを節約したい場合は、文字列インターンと呼ばれる概念で同じメモリを使用するように指示できます。

以下のルールは、概念を明確に理解するのに役立ちます。

  1. 文字列クラスは、最初は空のインターンプールを維持します。このプールは、一意の値のみを持つ文字列オブジェクトを含むことを保証する必要があります。
  2. 同じ値を持つすべての文字列リテラルは、区別の概念がないため、同じメモリロケーションオブジェクトと見なす必要があります。したがって、同じ値を持つそのようなすべてのリテラルは、インターンプールに単一のエントリを作成し、同じメモリ位置を参照します。
  3. 2つ以上のリテラルの連結もリテラルです。(したがって、ルール#2が適用されます)
  4. オブジェクトとして(つまり、リテラル以外の他のメソッドによって)作成された各文字列は、異なるメモリロケーションを持ち、インターンプールにエントリを作成しません。
  5. 非リテラルとリテラルを連結すると、非リテラルになります。したがって、結果のオブジェクトは新しいメモリ位置を持ち、インターンプールにエントリを作成しません。
  6. 文字列オブジェクトでインターンメソッドを呼び出すと、インターンプールに入る新しいオブジェクトが作成されるか、同じ値を持つ既存のオブジェクトがプールから返されます。インターンプールにないオブジェクトの呼び出しは、オブジェクトをプールに移動しません。むしろ、プールに入る別のオブジェクトを作成します。

例:

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

注:文字列インターンの動機付けのケースについては、ここでは説明しません。ただし、メモリの節約は間違いなく主要な目的の1つです。


#3をありがとう、知ら
なかった

4

コンパイル時と実行時の2つの期間を作成する必要があります。次に例を示します。

//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がリテラル文字列のプールに配置するため、jvm find "test"が存在する場合、例1では、存在するものを使用します。「テスト」文字列はすべて同じメモリアドレスを指しているため、例1ではtrueが返されます。一方、例2では、​​substring()のメソッドが実行時に実行されます。 "test" == "!test" .substring(1)の場合、プールは2つの文字列オブジェクトを作成します " test "と"!test "は異なる参照オブジェクトなので、この場合はfalseを返します。" test "=="!test ".substring(1).intern()の場合は、intern( ) ""!test ".substring(1)"をリテラル文字列のプールに入れます、


3

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

文字列インターニングは、各文字列値のコピーを1つだけ格納する方法であり、不変である必要があります。文字列をインターンすると、一部の文字列処理タスクの時間効率やスペース効率が向上しますが、文字列の作成またはインターン時に多くの時間を必要とします。個別の値は文字列インターンプールに格納されます。


2

インターンされた文字列は、文字列の重複を避けます。Interningは、重複する文字列を検出して置き換えるためにCPU時間を増やす代わりにRAMを節約します。参照がいくつ参照されていても、インターンされた各Stringのコピーは1つだけです。文字列は不変であるため、2つの異なるメソッドが偶然同じ文字列を使用する場合、それらは同じ文字列のコピーを共有できます。複製を共有に変換するプロセスは interning.String.intern()と呼ば列文字列、正規のマスター文字列のアドレスを提供します。インターンされた文字列を単純な==(ポインタを比較する)で比較することができますイコールの文字列の文字を1つずつ比較します。文字列は不変であるため、たとえば、「hippopotamus」などの他のリテラルの部分文字列として存在する場合、「pot」用の個別の文字列リテラルを作成しないなど、インターンプロセスはさらに領域を節約できます。

詳細を見るにはhttp://mindprod.com/jgloss/interned.html


2
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

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

2つの文字列が個別に作成さintern()れると、それらを比較できるようになります。また、参照が以前に存在しなかった場合に文字列プールに参照を作成するのにも役立ちます。

を使用するとString s = new String(hi)、javaは文字列の新しいインスタンスを作成しますが、String s = "hi"と、コードに「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

2
    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 intern()メソッドは、文字列定数プールにヒープ文字列オブジェクトの正確なコピーを作成するために使用されます。文字列定数プール内の文字列オブジェクトは自動的にインターンされますが、ヒープ内の文字列オブジェクトはインターンされません。インターンを作成する主な用途は、メモリスペースを節約し、文字列オブジェクトの比較を高速化することです。

ソース:Javaの文字列インターンとは何ですか?


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

s1そしてs22つの文字列プール「こんにちは」を指しているオブジェクト、および使用である"Hello".intern()ことがわかりますs1s2。つまり"s1 == s3"、trueとを返しますs3.intern()


これは実際には多くの新しい情報を提供しません。すでに例外的な答えがあります。
アレクサンダー

0

ヒープオブジェクト参照を使用して、対応する文字列定数プールオブジェクト参照を取得する場合は、intern()を使用する必要があります

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

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

ピクトリアルビュー ここに画像の説明を入力してください

ステップ1: データ「Rakesh」を持つオブジェクトは、ヒープと文字列定数プールに作成されます。また、s1は常にヒープオブジェクトを指しています。

ステップ2: ヒープオブジェクト参照s1を使用して、対応する文字列定数プールオブジェクト参照s2を取得しようとしています(intern()を使用)

ステップ3: 名前s3で参照される文字列定数プールにデータ「Rakesh」を持つオブジェクトを意図的に作成する

「==」演算子は参照比較を目的としています。

取得偽の S1 == s2のために

取得 S2 == s3のために

この助けを願っています!!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.