Stringクラス内部表現についての自分メモ
Stringのインスタンスメソッドsubstring(int)が呼び出されるときの内部挙動についての調査結果。
substring()が返すStringオブジェクトは全部新規にメモリ上に生成されるのか、それとも元のメンバを一部共有するのか。
結論を先に言うと、substring()で返された部分文字列は元のオブジェクトと同じメモリ領域を共有している。
まず、String.javaの中身を覗くと実装が以下のようになっていて、内部フィールドはchar配列と整数の組で構成されていた。
次にsubstring(int)を見ると、ここではオーバーロードしたメソッドが呼び出され、
そしてsubstring(int, int)の実装がこうなっていた。
通常はnewした時点で新規の領域が割り当てられるが、この場合はコンストラクタString(int, int, char[])の実装が以下のようになっており、
元のchar配列が再利用されて整数の組で新規の部分文字列を表現する仕様となっていた。つまり「Stringオブジェクトが毎回生成されるが、その実体であるchar配列は共有される。」が正解らしい。
そして、Stringのprivateメンバchar[]は変更されない仕様らしい。
わざわざchar[]を複製しないことを知って、賢いJavaにますます愛着がわいた。
こういうところが文字列処理のアルゴリズムにとってかなり重要。
Stringのインスタンスメソッドsubstring(int)が呼び出されるときの内部挙動についての調査結果。
substring()が返すStringオブジェクトは全部新規にメモリ上に生成されるのか、それとも元のメンバを一部共有するのか。
結論を先に言うと、substring()で返された部分文字列は元のオブジェクトと同じメモリ領域を共有している。
まず、String.javaの中身を覗くと実装が以下のようになっていて、内部フィールドはchar配列と整数の組で構成されていた。
public final class String { /** The value is used for character storage. */ private char value[]; /** The offset is the first index of the storage that is used. */ private int offset; /** The count is the number of characters in the String. */ private int count; ...
次にsubstring(int)を見ると、ここではオーバーロードしたメソッドが呼び出され、
public String substring(int beginIndex) { return substring(beginIndex, count); }
そしてsubstring(int, int)の実装がこうなっていた。
public String substring(int beginIndex, int endIndex) { //配列indexの範囲処理をした後↓ return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); }
通常はnewした時点で新規の領域が割り当てられるが、この場合はコンストラクタString(int, int, char[])の実装が以下のようになっており、
// Package private constructor which shares value array for speed. String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
元のchar配列が再利用されて整数の組で新規の部分文字列を表現する仕様となっていた。つまり「Stringオブジェクトが毎回生成されるが、その実体であるchar配列は共有される。」が正解らしい。
そして、Stringのprivateメンバchar[]は変更されない仕様らしい。
わざわざchar[]を複製しないことを知って、賢いJavaにますます愛着がわいた。
こういうところが文字列処理のアルゴリズムにとってかなり重要。