String.intern()
メソッドを知っているか、少なくとも聞いたことがあると
String.intern()
ます。 しかし、誰もがアプリケーションでそれを使用したり、どの場合にそれが有用で、まったく有用であるかを想像しているわけではありません。 そのため、プロジェクトの1つでこの方法に出くわすまでは一緒でした。 その瞬間、私はその使用の意味と特徴を知りたいと思い、Yahoo!の主要な開発者による非常に興味深い記事に出会いました。 Ethan Nicholasの名前で、その翻訳を、Java言語に無関心でないHabrコミュニティのその部分と共有したいと思います。
伝聞だけでこの方法を知っている人は、猫の下で歓迎します。
文字列は現代のプログラミング言語の基本部分であり、数字と同じくらい重要です。 したがって、JavaプログラマーはJavaプログラマーについて堅実な考えを持っている必要があると想定できますが、残念なことに、これは常に当てはまるわけではありません。
今日、 Xerces (Javaに含まれるXMLパーサー)のソースコードを見て、私を驚かせた行に出会いました。
com.sun.org.apache.xerces.internal.impl.XMLScanner:395
protected final static String fVersionSymbol = "version".intern();
次に、これと定義された行がさらにいくつか見つかり、それぞれがインターンになりました 。 では、
intern()
とは何ですか? 間違いなくご存じのとおり、Javaでオブジェクトを比較するには2つの異なる方法があります。
==
演算子を使用するか、
equals()
メソッドを使用できます。
==
演算子は、2つの参照が同じオブジェクトを参照しているかどうかを比較し、
equals()
は、2つのオブジェクトに同じデータが含まれているかどうかを比較します。
Javaを学習するときに最初に学ぶことの1つは、2つの文字列を比較するために
==
ではなく
equals()
使用することです。 たとえば、
new String("Hello") == new String("Hello")
と比較すると、これらはクラスの2つの異なるインスタンスであるため、結果は
false
です。
equals()
を使用する
equals()
、期待どおり
true
になり
true
。 残念ながら、
equals()
は文字ごとの文字列比較を実行するため、非常に遅くなる可能性があります。
なぜなら
==
演算子はIDをチェックし、2つのポインターを比較するだけでよく、明らかにこれは
equals()
よりもはるかに高速
equals()
。 したがって、同じ行を複数回比較する場合、文字比較の代わりにオブジェクトIDチェックを使用することで、パフォーマンスを大幅に向上させることができます。
主なアルゴリズム:
1)文字列のハッシュセットを作成する
2)扱っている文字列(文字のシーケンスとして)がすでにセットにあることを確認します
3)はいの場合、セットの文字列を使用します
4)それ以外の場合は、この文字列をセットに追加してから使用します
このアルゴリズムを使用すると、2つの文字列が同一の文字シーケンスである場合、クラスの1つのインスタンスであることが保証されます。 これは、
equals()
代わりに
==
を使用して文字列を安全に比較できることを意味しますが、繰り返しの比較よりもパフォーマンスが大幅に向上します。
幸いなことに、Javaにはすでにこのアルゴリズムの実装が含まれています。 これは、
java.lang.String
クラスの
intern()
メソッドです。 式
new String("Hello").intern() == new String("Hello").intern()
は
true
返し、
intern()
を使用せずに
false
返し
false
。
なぜ私は見るのに驚いたのですか
protected final static String fVersionSymbol = "version".intern();
Xercesソースコードに? 明らかに、この行は複数の比較に使用されます。 彼女をインターンすることは理にかなっていますか?
もちろんそうです。 これが、Javaがすでにこれを行う理由です。 クラスで発生するすべての定数文字列は自動的にインターンされます。 これには、独自の定数(上の
"version"
行など)と、クラスファイル形式の一部である他の行(クラス名、メソッドシグネチャなど)の両方が含まれます。 これは式にも適用されます。
"Hel" + "lo"
javac
によって
"Hello"
と同じ方法で処理されるため、
"Hel" + "lo" == "Hello"
は
true
返し
true
。
したがって、定義により、タイプ
"version"
定数文字列で
intern()
を呼び出した結果は、宣言したものとまったく同じオブジェクトになります。 つまり、
"version" == "version".intern()
常にtrueです。 文字列が定数ではない場合、インターンをインターンする必要があり、他のインターンされた文字列とすばやく比較できるようにしたい。
また、文字列をインターンするとき、次のようにメモリ使用量の利点を得ることができます。 このシーケンスを何度参照しても、文字列内の一連の文字のインスタンスを1つだけ格納します。 これが、クラスファイルの文字列定数がインターンされる主な理由です。たとえば、
java.lang.Object
など、参照するクラスの数を考え
java.lang.Object
。 クラス名
java.lang.Object
はこれらの各クラスに表示されますが、
intern()
魔法のおかげで、メモリ内の1つのインスタンスにのみ表示されます。
結論は?
intern()
は便利な方法であり、生活を楽にすることができますが、適切に使用するようにしてください。
翻訳者から
(私が思ったように)理解しやすくするために、ソーステキストを数回歪ませたことをおaびします。
Habrasocietyに私を招待してくれたノーブルhabrayuzerに感謝します。
更新する
他のソースから学んだ次の情報は、ここでは不必要ではないと思います。
1.文字列プールは、「Perm Gen」領域に保存されます。この領域は、非ユーザーJVMオブジェクト(クラスなど)用に予約されています。 これを無視すると、予想外にOutOfMemoryエラーが発生する場合があります。
2.抑留ラインは永久に保存されません。 参照されていない行もガベージコレクターによって削除されます。
3.ほとんどの場合、文字列比較がアプリケーションの主要な(または非常に頻繁な)操作であり、比較される文字列の長さが異なる場合を除き、intern()を使用してもパフォーマンスが大幅に向上することはありません。