Java学習 StringとStringBuilderから学ぶimmutableとmutable
※未経験初学者が書いている勉強まとめ記事なので、信用に足らない情報である可能性があります。
[StringとStringBuilderの違い]
完結に書くと、
Stringはimmutable(不変)
StringBuilderはmutable(可変)
提供されているメソッドでreplaceなどはメソッド名は同じだが挙動が違う。
StringBuilderはバッファを持つ。
このimmutableとmutableはメモリ上でどのような違いを見せるのでしょうか。
[String型のようなimmutableオブジェクトの場合]
//Stringは不変オブジェクト
//だから内容を変更する度に新しくインスタンスが生成される
String str1 = "str1はメモリ100番にあります";
str1 = "str1は101番にありますが、100番にも文章が残っています";
str1 = "str1はメモリ102番にありますが、100番と101番にも(略)";
//Stringでの文字連結は非効率?
String sen1 = "私の";
String sen2 = "名前は";
String sen3 = "ボボボーボ・ボーボボです";
//sen1 sen2 sen3 インスタンスを作成。
//sen1 + sen2 インスタンスを作成。
//(sen1 + sen2) + sen3インスタンスを作成。計5つ。
※実際は参照型なのでスタックとヒープの関係がありますが、ヒープのみで説明します。
①メモリ100番に"str1はメモリ100番にあります"を書き込み、str1とラベルをつける。
②メモリ101番に"str1は101番にありますが、100番にも文章が残っています"と書き込み、str1ラベルを101番につけなおす。
③メモリ102番に(略)
という風に、一見すると最初に確保されたメモリ100番で全ての書き換え作業が行われているように見えますが、新規にインスタンスがどんどん作られています。
以下、Stringのソースコードを一部抜粋。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
@Stable
private final byte[] value;
private final byte coder;
private int hash; // Default to 0
private boolean hashIsZero; // Default to false;
public String() {
this.value = “”.value;
this.coder = “”.coder;
}
public String(char value[], int offset, int count) {
this(value, offset, count, rangeCheck(value, offset, count));
}
わかるようなわからないようなです。
immutableオブジェクトの要素を満たしていて、
①クラスがfinalで修飾されている。
→サブクラスはfinalで修飾されたクラスを継承できないので値を変更できない。
②setterを提供しない。(のでnewしたとき以外に値は変わらない)
③全てのフィールドがprivate。(なのでアクセスできない)
④内部に可変オブジェクトを保持している場合、それを外部に提供しない。(getterなど)
よってStringはimutableオブジェクトであることも確認できました。
(setterやgetterの確認をしたい方はソースコードを全て読んでください。)
(④だけ少し曖昧ですが、不変オブジェクトを生成するクラスだと思っていたのにいきなりココだけは可変なんですと言われたら困惑しそう。
だからある可変オブジェクトを得たいなら別で目的の可変オブジェクトを提供するクラスを作った方が良いということでしょうか。)
[StringBuilder型のような可変オブジェクト]
StringBuiderのスーパークラスであるAbstractStringBuiderのフィールド。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
byte[] value;
byte coder;
int count;
Stringとprivateがない以外はほぼ同様のフィールド。booleanがない?
一応ソースにも当たってみましたぜ・・・くらいです。
これは文字をバイトに変換して文字コードをその後指定したりしているのかな・・・?
と、とりあえずprivateではないですよね、うん。
//StringBuilderは同じメモリ番地でやりとりをする。
StringBuilder sb1 = new StringBuilder("りんご");
sb1.replace(0,sb1.length(),"ばなな");
これでは理論的な証明にはなりませんね・・・。
とりあえずソースが読めるようになるまでは、同じメモリ番地でやり取りをしているんだなあくらいで覚えておきます。
ちなみに、StringBuiderはスレッドセーフですが、StringBufferはスレッドセーフではありません。
どちらも同じAbstractStringBuiderをスーパークラスに持っているので同じように使うことができますが、スレッドセーフかどうかだけは注意。
初期値のバッファを使い切ると初期値x2+2のバッファが生成されますが、当初から1000文字などの長文を想定しているなら大き目のバッファをあらかじめとっておいた方が容量確保処理をする必要がない分効率がいいそうです。
この記事が気に入ったらサポートをしてみませんか?