プログラムのコードには、多くの文字列リテラルが含まれている傾向があります。Javaでは、これらの定数は効率を上げるために文字列テーブルと呼ばれるものに収集されます。たとえば、文字列"Name: "
を10の異なる場所で使用する場合、JVMには(通常)その文字列のインスタンスが1つだけあり、使用される10の場所すべてで、参照はすべてその1つのインスタンスを指します。これにより、メモリが節約されます。
String
は不変であるため、この最適化が可能です。文字列を変更できる場合、1つの場所を変更すると、他の9つの文字列も変更されることになります。そのため、文字列を変更する操作はすべて新しいインスタンスを返します。そのため、これを行うと次のようになります。
String s = "boink";
s.toUpperCase();
System.out.println(s);
boink
ではなく、印刷しBOINK
ます。
ここでもう1つ注意が必要な点があります。の複数のインスタンスは、文字データの基になる同じものを指しているjava.lang.String
可能性があります。つまり、配列のスライスだけを使用することで、同じ上の異なるビューである可能性があります。繰り返しますが、効率の最適化。この方法は、これが発生するケースの1つです。char[]
char[]
substring()
s1 = "Fred47";
//String s1: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=0, length=6
// ^........................^
s2 = s1.substring(2, 5);
//String s2: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=2, length=3
// ^.........^
// the two strings are sharing the same char[]!
SCJPの質問では、これはすべて次のように要約されます。
- 文字列
"Fred"
は文字列テーブルから取得されます。
- 文字列
"47"
は文字列テーブルから取得されます。
- 文字列
"Fred47"
は、メソッド呼び出し中に作成されます。// 1
- 文字列
"ed4"
はメソッド呼び出し中に作成され、//2と同じバッキング配列を共有します"Fred47"
- 文字列
"ED4"
は、メソッド呼び出し中に作成されます。// 3
- は
s.toString()
新しいものを作成せず、単にを返しますthis
。
このすべての興味深いエッジケースの1つ:たとえば、インターネットから取得したWebページなど、非常に長い文字列がある場合にどうなるかを考えてみましょう。たとえば、の長さchar[]
が2メガバイトであるとします。これを利用すると、4文字の長さのように見えるsubstring(0, 4)
新しい文字列が得られますが、それでも2メガバイトのバッキングデータを共有します。これは現実の世界ではそれほど一般的ではありませんが、メモリの大きな浪費になる可能性があります。問題としてこれに遭遇する(まれな)ケースでは、新しい小さなバッキング配列を使用して文字列を作成するために使用できます。new String(hugeString.substring(0, 4))
最後に、文字列を呼び出すことにより、実行時に文字列を文字列テーブルに強制的に入れることができますintern()
。この場合の基本的なルール:それをしないでください。拡張ルール:メモリプロファイラーを使用して、それが有用な最適化であることを確認していない限り、これを行わないでください。