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



All Articles