web-dev-qa-db-ja.com

StringリテラルでStringのインターンメソッドを使用する場合

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

S3がインターンされるとs1 and s3 are sameが印刷され、s1 and s2 are sameは印刷されないと予想していました。しかし、結果は次のとおりです。両方の行が印刷されます。つまり、デフォルトでは文字列定数がインターンされます。しかし、そうであれば、なぜinternメソッドが必要なのでしょうか?言い換えれば、このメソッドをいつ使用する必要がありますか?

182
Rakesh Juyal

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" を参照してください。

224

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

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

欠点は、ストリングを単純にヒープに投げるよりも時間がかかること、およびJava実装によっては、閉じ込められたストリングのスペースが制限される場合があることです。多くの重複がある既知の合理的な数の文字列を扱う場合に最適です。

19
Carl Smotricz

インターンされた文字列で==を使用して2セントを追加したいと思います。

String.equalsが最初に行うことはthis==objectです。

多少のパフォーマンスの向上はありますが(メソッドを呼び出していません)、メンテナーの観点から==を使用することは悪夢です。

そのため、インターンされた文字列には==の特殊なケースに依存しないことをお勧めしますが、Goslingが意図したように常にequalsを使用します。

編集:非インターンになるインターン:

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は引き続き機能します。繰り返しますが、パフォーマンスの向上はごくわずかですが、メンテナンスコストが高くなります。

14

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

12
Bozho

Java文字列インターンを学ぶ-一度だけ

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

以下の規則は、明確な用語で概念を理解するのに役立ちます:

  1. 文字列クラスは、最初は空のインターンプールを保持しています。このプールは、一意の値のみを持つ文字列オブジェクトを含むことを保証する必要があります。
  2. 同じ値を持つすべての文字列リテラルは、他の点では区別の概念がないため、同じメモリ位置オブジェクトと見なす必要があります。したがって、同じ値を持つすべてのそのようなリテラルは、インターンプールに単一のエントリを作成し、同じメモリロケーションを参照します。
  3. 2つ以上のリテラルの連結もリテラルです。 (したがって、ルール#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

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

6
KGhatak

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

4
thinkinjava

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

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

3
bbcbcgb
    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.
}
2
Anurag_BEHS
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
anish

インターンされた文字列は、文字列の重複を避けます。インターンは、重複する文字列を検出して置換するためのCPU時間を犠牲にしてRAMを節約します。参照されている参照の数に関係なく、インターンされた各ストリングのコピーは1つのみです。文字列は不変であるため、2つの異なるメソッドが偶然同じ文字列を使用する場合、同じ文字列のコピーを共有できます。重複した文字列を共有文字列に変換するプロセスは、interning.String.intern()と呼ばれ、正規のマスター文字列のアドレスを提供します。ストリングの文字を1つずつ比較するequalsの代わりに、インターンされたストリングを単純な==(ポインターを比較する)で比較できます。文字列は不変であるため、たとえば、「カバ」などの他のリテラルのサブストリングとして存在する場合、「ポット」に別の文字列リテラルを作成しないことにより、インターンプロセスはスペースをさらに節約できます。

もっと見るには http://mindprod.com/jgloss/interned.html

2
inter18099
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"を使用すると、JavaはWordのインスタンスがあるかどうかを確認します "コード内で「hi」を使用するかどうかを指定し、存在する場合は参照を返します。

文字列の比較は参照に基づいているため、intern()は参照の作成に役立ち、文字列の内容を比較できます。

コードでintern()を使用すると、同じオブジェクトを参照する文字列が使用するスペースがクリアされ、メモリ内に既に存在する同じオブジェクトの参照が返されます。

ただし、p5を使用している場合:

String p5 = new String(p3);

P3のコンテンツのみがコピーされ、p5が新しく作成されます。 internedではありません。

したがって、出力は次のようになります。

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
raja emani

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

ソース: javaの文字列インターンとは?

1
user2485429

あなたが言ったように、その文字列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」を指す2つのオブジェクトであり、"Hello".intern()を使用すると、s1s2が見つかります。そのため、"s1 == s3"s3.intern()と同様にtrueを返します。

1
WendellWu