String.internのすべて()

多くのJava開発者が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()を使用してもパフォーマンスが大幅に向上することはありません。

Source: https://habr.com/ru/post/J79913/


All Articles