Search on the blog

2013年9月16日月曜日

Stringインスタンスの共有

 JavaのStringインスタンスの共有についていまいち理解していなかったので、JLSを読んでみた。
まず、JLSに書いているサンプルプログラムを動かしてみる。

[Test.java]
package testPackage;

class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.println(hello == "Hello");
        System.out.println(Other.hello == hello);
        System.out.println(other.Other.hello == hello);
        System.out.println(hello == ("Hel" + "lo"));
        System.out.println(hello == ("Hel" + lo));
        System.out.println(hello == ("Hel" + lo).intern());
    }
}

class Other {
    static String hello = "Hello";
}

[Other.java]
package other;

public class Other {
    public static String hello = "Hello";
}
上記プログラムの実行結果は以下のとおり。
$ true
$ true
$ true
$ true
$ false
$ true

ほう。これは毎回こうなるのだろうか?
JLS 3.10.5には以下のように書いている。

Literal strings within the same class in the same package represent references to the same String object.
Literal strings within different classes in the same package represent references to the same String object.
Literal strings within different classes in different packages likewise represent references to the same String object.
Strings computed by constant expressions are computed at compile time and then treated as if they were literals.
Strings computed by concatenation at run time are newly created and therefore distinct. 

まとめるとだいたい下のような感じ。
  • 文字列の内容が同じリテラルはクラス/パッケージが違っても同じインスタンスを指す。 
  • コンパイル時に計算される文字列はリテラルと同様に扱われる。
  •  実行時に結合によって生成される文字列は新しいインスタンス化されるため、別インスタンスとなる。
なるほどー。同じリテラルの場合、インスタンスも同じになるかどうかはタイミングとかによるのかなと思い込んでいたけど、常に同一インスタンスになるのか。

JLSには以下のような記述もある。
String literals-or, more generally, strings that are the values of constant expressions -are "interned" so as to share unique instances, using the method String.intern.

internすることでリテラルは同じインスタンスが共有されるらしい。上のサンプルプログラムでもconcatenateした文字列を明示的にinternすることでインスタンスが共有されている。このinternとは何ぞや? Javadocを見てみる。

Returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class String. When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. It follows that for any two strings s and t, s.intern() ==t.intern() is true if and only if s.equals(t) is true. All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java Language Specification.
なるほど、concatenateした場合は新しいインスタンスができるけど、プールの中を検索してequalsでヒットしたらそのインスタンスが返されるってことか。

0 件のコメント:

コメントを投稿