【java】StringとStringBuilderの違いを整理(javaシルバー資格取得に向けて)


はじめに

Rubyを数ヶ月かじった人間が、javaシルバー資格取得の為に学習を進めています。
そのメモです。
もし、認識違いとかがあればご指摘いただけますと幸いです!

StringクラスとStringBuilderクラスの違い(ざっくり)

そもそも全くの別物

名前は似ているから、継承関係でもあるのかな?と思っていましたが、そんな事ありませんでした。
インタフェースの実装については一部被ってはいますね。

▼Stringクラス

▼StringBuilderクラス

用意されているメソッドが違う

そもそも全くの別物なので、String〜StringBuilderメソッド間での継承などはありません。
ただ、一部のインタフェースは両方のクラスで実装しているので「メソッド名が一緒だけど処理内容が違う。」なんてメソッドがあります。
この辺りが多分混乱のタネなんですかね?(僕だけ?)

値の書き換え

Stringをデータ型として定義した変数

aiueo変数の中身を"あいうえお"から"かきくけこ"へ変更
 

"かきくけこ"が出力される。
表面上は書き換えができている。
けど実は、変数aiueoはあいうえおという場所じゃなくてかきくけこという場所を見るように参照先を切り替えてる。
つまり、実際は書き換えてなくて、新しくかきくけこの場所を参照するように切り替えているだけで、あいうえおさんは健在。おめぇの席ねぇから。状態。
なので、変数に値を入れ直す度にメモリを消費しているらしい。

加えて、気をつけたいところとしては、明示的に代入演算子で代入してあげないと変数の中身(呼び出し先)はかわらない。

StringSample_concat.java
        String s1 = "あいうえお";
        s1.concat("カキクケコ");
        System.out.println(s1);
        // これだと"あいうえお"と表示される
        s1 = s1.concat("かきくけこ");
        System.out.println(s1);
        //代入演算子を使って、明示的に処理結果をs1に代入してあげないとs1の中身は書き換わらない

一見s1.concat("カキクケコ");で「よっしゃ、s1の中身書き換えたで」って満足しがち。
しっかり代入演算子を使ってs1 = s1.concat('かきくけこ')としないと、s1の中身は変わらない。

StringBuilderをデータ型として定義した変数

sb1変数の中身を"あいうえお"から"かきくけこ"へ変更

"かきくけこ"が出力される。
実際にsb1の中身が書き換わっていることになる。
変数の中身を変える度にメモリを消費しないので、処理負荷を考えるとこっちの方が優男に見えます(笑

メソッド一例

StringとStringBuilderとで同じメソッド名だけど処理が違う。なんてものが存在します。
同じインタフェースをそれぞれが実装しているので、処理内容がそれぞれで異なってくることもあるんです。
なので、一例を。。。

メソッド名が一緒だけど全然扱いが違うパターン

replace()メソッド

Stringの場合

▼処理内容
対象の文字列を、指定した文字列に書き換える

▼引数
第一引数(String):変換対象の文字
第二引数(String):変換対象に該当した文字を何に変換するか指定
▼注意点
第一引数に該当する全ての文字列を対象としていること
(最初の一個だけじゃないヨ)

String.java
   String a1 = "aiueoaiueoaiueo";
   /* "第一引数: 書き換えたい文字 "
      "第二引数: 第一引数にマッチした文字をこの引数に書き換える" */
   String a2 = a1.replace("a", "書き替えた");
   System.out.println("a2 = " + a2);
   /* 出力結果
      a2 = 書き替えたiueo書き替えたiueo書き替えたiueo */

StringBuilderの場合

▼処理内容
引数で指定した範囲を対象に、指定した文字列を書き換える
▼引数
第一引数(int):変更範囲の開始位置(0が最初)
第二引数(int):変更範囲の終了位置
第三引数(String):第一引数と第二引数で指定した範囲に対して、書き換えたい文字列

StringBuilder.java
        StringBuilder sb1 = new StringBuilder("あいうえおAいうえおIいうえおあいうえお");
        sb1.replace(0, 10, "かきくけこ");
        /* 先頭から10文字目までを対象に"かきくけこ"に書き換える*/
        System.out.println(sb1);
        /*出力結果
        かきくけこIいうえおあいうえお*/

メソッド名も扱いも変わらないパターン

indexOf()メソッド

String,StringBuilderも処理結果は同じです。
第二引数(int)を指定することで、検索範囲を絞ることもできます。

StringBuilder_indexOf.java
        StringBuilder sb1 = new StringBuilder("あいうえおAいうえおIいうえおあいうえお");
        String s1 = "あいうえおAいうえおIいうえおあいうえお";

        System.out.println(s1.indexOf("Aいうえお"));
        // 出力結果: 5
        System.out.println(sb1.indexOf("Aいうえお"));
        // 出力結果: 5

そのほか変わらないパターン

substring(),length(),lastIndexOf() などなど・・・
挙げ出すとキリがなさそうなので公式リファレンスを参照しましょう。

とりあえずはreplace()メソッドに気をつけよう

一つ一つ比較していくとキリがないのと、比較的問題に出やすいのとを考えると、一旦はreplace()メソッドだけでも抑えておくとよさそう。

まとめ

最後に、試験を受ける上で大事そうなことをまとめてみます。

  • String変数は表面上中身を変えてるけど、書き換わってはいないので変える度にメモリを食う。
  • String変数に対してメソッドを使って文字列操作をした場合は、明示的に代入演算子で書き換え(参照先を変える)あげないと、中身は変わらない!
  • StringとStringBuilderは一部同じインタフェースを実装しているので、メソッド名が同じだけど処理が違うメソッドがある!
  • javaシルバーの問題対策としては、replace()メソッドの扱い方が違うことをしっかり頭に入れておけば一旦は大丈夫そう?

ほかにも「これも覚えておきな!」や、「それちょっと認識違うよ!」などあればご指摘いただけますと幸いですm(_ _)m
それでは、ありがとうございました。