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
メソッドが必要なのでしょうか?言い換えれば、このメソッドをいつ使用する必要がありますか?
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" を参照してください。
最近のプロジェクトでは、いくつかの巨大なデータ構造が、データベースから読み込まれたデータ(したがって、文字列定数/リテラルではない)で設定されましたが、大量の重複がありました。それは銀行のアプリケーションであり、ささやかなセット(おそらく100または200)の企業の名前のようなものが至る所に現れました。データ構造は既に大きく、それらの企業名がすべて一意のオブジェクトだった場合、メモリがオーバーフローしていました。代わりに、すべてのデータ構造が同じ100または200のStringオブジェクトへの参照を持っているため、多くのスペースを節約できます。
インターンされた文字列のもう1つの小さな利点は、関連するすべての文字列がインターンされることが保証されている場合、==
を使用して文字列を比較できることです。より簡潔な構文とは別に、これはパフォーマンスの向上でもあります。 ただし他の人が指摘しているように、これを行うとプログラミングエラーが発生するリスクが大きくなるため、これは最終手段の別の手段としてのみ行う必要があります。
欠点は、ストリングを単純にヒープに投げるよりも時間がかかること、およびJava実装によっては、閉じ込められたストリングのスペースが制限される場合があることです。多くの重複がある既知の合理的な数の文字列を扱う場合に最適です。
インターンされた文字列で==
を使用して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
は引き続き機能します。繰り返しますが、パフォーマンスの向上はごくわずかですが、メンテナンスコストが高くなります。
文字列リテラルと定数はデフォルトでインターンされます。つまり、"foo" == "foo"
(文字列リテラルで宣言)ですが、new String("foo") != new String("foo")
です。
Javaの文字列は、設計上不変オブジェクトです。したがって、同じ値を持つ2つの文字列オブジェクトは、デフォルトで異なるオブジェクトになります。ただし、メモリを節約する場合は、文字列インターンと呼ばれる概念によって同じメモリを使用するように指示できます。
以下の規則は、明確な用語で概念を理解するのに役立ちます:
例:
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つです。
コンパイル時間とランタイム時間の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を返します。
http://en.wikipedia.org/wiki/String_interning
文字列インターンは、各文字列値のコピーを1つだけ保存する方法であり、不変でなければなりません。文字列をインターンすると、文字列を作成またはインターンする際により多くの時間が必要になりますが、一部の文字列処理タスクの時間効率またはスペース効率が向上します。個別の値は、文字列インターンプールに格納されます。
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 = "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
インターンされた文字列は、文字列の重複を避けます。インターンは、重複する文字列を検出して置換するためのCPU時間を犠牲にしてRAMを節約します。参照されている参照の数に関係なく、インターンされた各ストリングのコピーは1つのみです。文字列は不変であるため、2つの異なるメソッドが偶然同じ文字列を使用する場合、同じ文字列のコピーを共有できます。重複した文字列を共有文字列に変換するプロセスは、interning.String.intern()と呼ばれ、正規のマスター文字列のアドレスを提供します。ストリングの文字を1つずつ比較するequalsの代わりに、インターンされたストリングを単純な==(ポインターを比較する)で比較できます。文字列は不変であるため、たとえば、「カバ」などの他のリテラルのサブストリングとして存在する場合、「ポット」に別の文字列リテラルを作成しないことにより、インターンプロセスはスペースをさらに節約できます。
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
string intern()メソッドは、文字列定数プールにヒープ文字列オブジェクトの正確なコピーを作成するために使用されます。文字列定数プール内の文字列オブジェクトは自動的にインターンされますが、ヒープ内の文字列オブジェクトは自動的にインターンされません。インターンを作成する主な用途は、メモリスペースを節約し、文字列オブジェクトの比較を高速化することです。
ソース: javaの文字列インターンとは?
あなたが言ったように、その文字列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
とs2
は、文字列プール「Hello」を指す2つのオブジェクトであり、"Hello".intern()
を使用すると、s1
とs2
が見つかります。そのため、"s1 == s3"
はs3.intern()
と同様にtrueを返します。