intern()は「文字列プールの中にその文字列があればそれを返し、なければ追加して返す」メソッドです。
Javaには同じ文字列が何度も使われることを想定して、メモリのムダをなくす工夫があります。それが「文字列プール(String Pool)」です。intern()
メソッドは、このプールと関係しています。
Javaの文字列リテラルと文字列プールとは?
Javaでは、文字列を "apple"
のようにダブルクオーテーションで書いたものを文字列リテラルと呼びます。
参考 リテラルの基本概念
String s = "apple";
このように書かれた文字列は、文字列プール(String Pool)という特別な場所に自動で保存されます。文字列プールとは、同じ文字列を一度だけ保存しておく場所、そしてそれを使いまわす仕組みです。
つまり、同じ "apple"
を何度使っても、同じ1つのインスタンスを共有することで、メモリの節約しよう!という考え方です。
new String()を使うと何が起こる?
次のようなコードを書いたとします。
String s = new String("apple");
このとき、"apple"
というリテラルはプールに登録されますが、new String(...)
は新しいインスタンスをヒープ領域に作成します。つまり、↑のコードは内部で次の2つの処理が行われています:
- リテラル部分
"apple"
:
ソースコード中の文字列リテラルは、クラスがロードされる際に自動的に文字列プールに登録されます。つまり、この時点で"apple"
は既にプール内に存在することになります。 - new String("apple"):
new String(...)
は、プールにあるリテラル"apple"
を元にしてヒープ上に新しい String オブジェクトを生成します。
※ この新たなオブジェクトはプールに登録されず、ヒープ上の独立した存在です。
比較するとこうなります:
String s1 = "apple"; // プール上の"apple" String s2 = new String("apple"); // ヒープ上の新しい"apple" System.out.println(s1 == s2); // false(参照が異なる)
intern()の役割とは?
intern()
メソッドを使うと、次のような動作をします:
「この文字列と同じ内容の文字列が文字列プールにあるかを確認し、あればそれを返す。なければプールに登録して返す」
つまり、new String(...)
のようにヒープに作られた文字列も、プールにある文字列と結びつけられるのです。
String s1 = "apple"; String s2 = new String("apple"); String s3 = s2.intern(); System.out.println(s1 == s2); // false(ヒープとプールで違う) System.out.println(s1 == s3); // true(どちらもプール)
- リテラル "apple":
クラスロード時に文字列プールに登録される(自動で管理される)。 - new String("apple"):
ヒープ上に新しいオブジェクトを生成。内部で使われているリテラルはプールされているが、この新オブジェクト自体はプールには入りません。 - new String("apple").intern():
プール内に既に "apple" があるので、その参照が返される(= リテラル "apple" と同じインスタンス)。
intern()を使うと何がうれしいの?
✅ メモリ効率が良くなる
同じ文字列を何度も使っている場合、それぞれが別のインスタンスだとメモリがムダになります。intern()
を使えば1つにまとめられます。
✅ ==で比較できるようになる
Javaでは==
は「中身」ではなく「参照(場所)」の比較です。intern()
でプールに統一しておけば、中身が同じ文字列は同じ参照になるため、==
で比較できるようになります。