2.10. java.lang.StringBuilder。編集できる文字列クラス

StringBuilderはStringと同じで文字列を扱いますが、大きな違いあります。

なんと?、内部文字列を変更することができます。

Stringは文字列固定なので、文字を編集したいときには常に新しいStringを生成しなくてはいけません。

これは、扱う文字列大きくなると処理が増えてしまい不利になります。

そこで、代わりにStringBuilderを使うとよろしい場面が出てきます。

2.10.1. クラス図へ変換

では、早速クラス図にしてみましょう。

UML変換くんを実行。

> touml .\java\lang\*StringBuilder.java

※java1.8コード

今回は、ワイルドカード付けて「*StringBuilder.java」としました。

親クラスのAbstractStringBuilderも一緒に表示しておきます。

まず、パッケージ図はこれです。

StringBuilderはlang内にあり、関連packageはこんな感じでした。

../_images/java.lang.StringBuilder.image001.jpg

sun.miscは「import sun.misc.FloatingDecimal;」を使っていました。

浮動小数点を文字列に変換する処理です。

クラス図です。 java.lang._StringBuilder.svg

元の図は大きいので小さく切り取って、見ていきます。

次にクラス図です。

../_images/java.lang._StringBuilder.svg

元の図は大きいので小さく切り取って、見ていきます。

2.10.2. 継承

../_images/java.lang.StringBuilder.image002.jpg

2.10.2.1. まずは親クラスを見てみる

まずは親クラスのAbstractStringBuilderから見てみます。

CharSequenceを実現しています。

CharSequenceは文字列型の共通インタフェースで、String型などもこれを実現しています。

文字列型なら、これを実現しろということでしょう。

Appendableは後ろに追加できる機能を持つクラスが実現するインタフェースです。

AbstractStringBuilderは次のようなメソッドをオーバーライドしています。

+append(s:CharSequence):AbstractStringBuilder

+append(s:CharSequence,start:int,end:int):AbstractStringBuilder

+append(c:char):AbstractStringBuilder

2.10.2.2. ターゲットのクラスを見る

../_images/java.lang.StringBuilder.image003.jpg

CharSequenceを実現しているのは親クラスAbstractStringBuilderと同じですね。

文字列型の共通インタフェースです。

StringBuilderはシリアライズしたいので、Serializableを実現しています。

後は、AbstractStringBuilderを継承しています。

わざわざAbstractStringBuilderを作ったのは、似たような機能のクラスStringBufferと処理を共用するためだと思われます。

StringBufferもAbstractStringBuilderを継承していました。

2.10.3. フィールドを見てみる

2.10.3.1. 親クラス

../_images/java.lang.StringBuilder.image004.jpg

~value:char[]は文字列本体を格納する場所です。

Stringクラスの場合はこれが、{raedOnly}だったです。

この辺が文字列を編集できるかできないかの違いです。

~count:intは文字数を格納します。

2.10.3.2. ターゲットのクラス

../_images/java.lang.StringBuilder.image005.jpg

~serialVersionUID:long=4383685877147921099L{readOnly}はシリアライズするクラスはこのUIDを決めて、

セットしてやる必要があります。

2.10.4. メソッドを眺めてみます

2.10.4.1. 親クラスAbstractStringBuilder

../_images/java.lang.StringBuilder.image006.jpg

コンストラクタですね。始めにサイズが分かっている場合にはcapacityをセットすると先にその分、領域を確保します。

../_images/java.lang.StringBuilder.image007.jpg

格納されている文字列サイズや、確保しているキャパシティサイズを取得できます。

../_images/java.lang.StringBuilder.image008.jpg

文字列を格納する領域(キャパシティ)を確保したり、拡張したりするメソッドです。

../_images/java.lang.StringBuilder.image009.jpg

無駄なキャパシティをカットして格納文字列と同じする機能ですね。

../_images/java.lang.StringBuilder.image010.jpg

これは、格納文字列数をセットする処理ですね。

格納文字列より大きな値を設定すると、差分を'0'で埋めていました。

これは実際、どんな場面で使うのかな?不思議です。

../_images/java.lang.StringBuilder.image011.jpg

この辺は指定位置の文字を取得したり、セットしたりする機能ですね。

../_images/java.lang.StringBuilder.image012.jpg

このへんはappendXXなので、文字列の末尾に文字列を追加する機能です。

../_images/java.lang.StringBuilder.image013.jpg

deleteXXは指定した位置の文字を削除して詰める機能です。 途中削除はSystem.arraycopyで対象文字列データをコピーする処理なのでデータが多いと負荷の高い処理ですね。

../_images/java.lang.StringBuilder.image014.jpg

replaceは指定した位置に指定した文字列を置き換える処理。

substring、subSequenceは指定した位置の文字列を返します。

../_images/java.lang.StringBuilder.image015.jpg

insert関連は、指定した位置に文字列を挿入します。

System.arraycopyで挿入位置から後ろの文字列をコピーするので、格納文字列のサイズが大きいと負荷が大きくなりますね。

../_images/java.lang.StringBuilder.image016.jpg

indexOfは前から、lastIndexOfは後ろから引数の文字列をサーチして見つかった位置を返却します。

../_images/java.lang.StringBuilder.image017.jpg

reverseは格納文字列を反転させるしょりです。

getValueは内部の格納配列をそのまま返却します。

2.10.4.2. ターゲットクラスStringBuilder

../_images/java.lang.StringBuilder.image018.jpg

コンストラクタです。

基本、親クラスのメソッドを呼んでいるだけですね。

こんな感じ「super(capacity);」。

../_images/java.lang.StringBuilder.image019.jpg

appendもほぼ親クラスのメソッドを呼んでいるだけです。

ただ、オーバーライドでStringBuilderを返却するようにしています。

親クラスではAbstractStringBuilderの返却です。

これは、「共変戻り値」の機能を使ったオーバーライドというそうです。

※c++のオーバーライドではできなかったので、ちょっと混乱。

※でも、確かに理にかなっていますね。

../_images/java.lang.StringBuilder.image020.jpg

このへんも、親クラスの処理を呼んでいるだけでね。

../_images/java.lang.StringBuilder.image021.jpg

これは、シリアライズとその復元に利用するメソッドだそうです。

2.10.5. 例外

../_images/java.lang.StringBuilder.image022.jpg

例外ExceptionはwriteObjectから、例外Exception、ClassNotFoundExceptionはreadObjectのメソッドから発生します。

※どちらの例外も、java.io.ObjectOutputStream、java.io.ObjectInputStreamのモノです。

2.10.6. まとめ

それにしても、StringBuilderの処理はほとんど親クラスのAbstractStringBuilderの処理でした。

そういうわけで、AbstractStringBuilderの調査も一緒に書きました。

親クラスを作ったのはStringBufferのためだと思いますので、

今度はこれも眺めてみます。

あと、途中挿入や削除で文字配列のコピーを使って実装していました。

なるべくappend以外は使わないほうが効率良さそうです。