193

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方法が必要なのでしょうか。言い換えれば、この方法をいつ使用する必要がありますか?

4

14 に答える 14

240

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使用して明示的に作成された値、およびその結果でメソッドが使用されなかった場合を除くすべての場合で、JVMの文字列定数プールが返されるのは単一の不変インスタンスです。newintern

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

于 2009-12-06T11:54:46.710 に答える
21

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

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

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

于 2009-12-06T11:59:40.003 に答える
16

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

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

したがって、パフォーマンスはわずかに向上しますが(メソッドを呼び出さない場合)、メンテナの観点からは==、一部のインターンされた文字列は非インターンになる傾向があるため、を使用することは悪夢です。

==したがって、インターン文字列の特殊なケースに依存するのではなく、常に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、引き続き機能します。繰り返しになりますが、パフォーマンスの向上はごくわずかですが、メンテナンスコストは高くなります。

于 2009-12-06T12:13:28.507 に答える
12

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

于 2009-12-06T11:53:15.467 に答える
12

Java文字列インターンを学ぶ-すべてに一度

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

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

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

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

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

于 2014-09-01T08:40:06.770 に答える
3

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

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

于 2013-09-12T18:47:28.137 に答える
2

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

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

于 2013-08-22T04:31:00.167 に答える
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
于 2013-11-12T10:37:18.850 に答える
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"と、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
于 2016-07-12T08:56:08.013 に答える
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.
}
于 2018-02-17T19:55:18.933 に答える
1

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

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

于 2015-03-31T17:30:44.543 に答える
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」を指す2つのオブジェクトであり、を使用するとそれと"Hello".intern()が見つかります。したがって、trueを返します。s1s2"s1 == s3"s3.intern()

于 2015-04-20T13:02:47.790 に答える
1

対応する文字列定数プールオブジェクト参照を取得する場合は、ヒープオブジェクト参照を使用して、 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を使用して、intern()を使用して、対応する文字列定数プールオブジェクト参照s2を取得しようとしています。

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

「==」演算子は参照比較用です。

s1==s2に対してfalseを取得する

s2 ==s3で真になる

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

于 2020-02-01T18:37:29.783 に答える