JavaでStringBuilderとStringBufferってどっち使えば良いの?

f:id:ts0818:20190507204100j:plain

今まで、まったく気にせずにきたんですが...あっ、どうもボクです。

Javaで、StringBuilder と、StringBuffer ってあるのは知ってたんだけど、どっちを使うべきかってあんまり意識してこなかったんですが。

というわけで、今回もJavaで。

 

スレッドセーフかスレッドセーフでないか

ズバリ、そういうことらしいっす。

totech.hateblo.jp

StringBuilderとStringBufferの実装の違いを見てみる。どちらもAbstractStringBuilderクラスを継承したクラスとなっている。
  • AbstractStringBuilderクラス : 実際の実装を持つ親クラス
    • StringBuilderクラス : スレッドセーフでないサブクラス
    • StringBufferクラス : スレッドセーフなサブクラス

【Java】StringBuilderとStringBufferの違いをスレッドセーフの観点で検証してみた - カタカタブログ

メソッドの違いはというと、

StringBuilderのappendメソッド

public StringBuilder append(char c) {
    super.append(c);
    return this;
}

StringBufferのappendメソッド

public synchronized StringBuffer append(char c) {
    super.append(c);
    return this;
}

【Java】StringBuilderとStringBufferの違いをスレッドセーフの観点で検証してみた - カタカタブログ  

どちらも親クラスのappendを呼び出して自身を返すという全く同じロジックとなっており、違いはsynchronizedがStringBuffer側にはあることのみ。StringBufferはappendメソッドがスレッドセーフな仕様となっている。

【Java】StringBuilderとStringBufferの違いをスレッドセーフの観点で検証してみた - カタカタブログ

結局ここの違いで、StringBufferのappendは同期のためにロック等が発生しているため、その分StringBuilderに比べてパフォーマンスが悪くなっている。

【Java】StringBuilderとStringBufferの違いをスレッドセーフの観点で検証してみた - カタカタブログ

⇧  StringBuffer は、マルチスレッドに対応してる分、処理速度が落ちるらしいとのこと。

www.javainthebox.net

StringBuffer クラスのすべてのメソッドは synchronized になっており、同期化されています。このため、複数のスレッドから使用される場合でも、安全に使用することができます。

その一方で同期化にはコストがかかります。簡単にいえばメソッドを synchronized にすると遅くなってしまうのです。

しかし、StringBuffer を使うときに本当にスレッドセーフが必要ですか?

私が書いてきたコードには StringBuffer クラスにスレッドセーフが必要だったケースはほとんどありませんでした。それなのにわざわざ同期化した StringBuffer クラスを 使う必要はありません。

そこで、登場したのがスレッドセーフではない StringBuilder クラスです。

J2SE 5.0 Tiger 虎の穴 StringBuilder

⇧  上記サイト様によりますと、StringBuilder 使っとけば大丈夫ってことらしいっす。

いままで、StringBuffer のほうが、処理が速いんだと思ってました(涙)。

 

というわけで、StringBuilder

Aizu Online Judge のStringに関する問題でトライ。

judge.u-aizu.ac.jp

Eclipseで、適当なJavaプロジェクト、クラスを作成で。

f:id:ts0818:20190503154443p:plain

こんな感じのコーディングになりました。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Transformation {

  public static void main(String[] args) {
    // TODO 自動生成されたメソッド・スタブ
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));    
    List<string> list = new ArrayList<>();
    
    try {
      // 対象の文字列
      StringBuilder bl = new StringBuilder(br.readLine());
      // 命令の回数
      int q = Integer.parseInt(br.readLine());
      
      for (int i = 0; i < q; i++) {
        // 命令(文字列の編集内容)
        String[] order = br.readLine().split("\\s");
        // 文字列を編集する開始位置
        int a = Integer.parseInt(order[1]);
        // 文字列を編集する終了位置
        int b = Integer.parseInt(order[2]) +1;
        
        if (order[0] != null) {
          switch (order[0]) {
          case "print" :   // a 文字目から b 文字目までを出力する。
            list.add(bl.substring(a, b).toString());  // 出力する内容をリストに保存
            break;
          case "reverse" : // a 文字目から b 文字目までを逆順にする。
            bl.replace(a, b, (new StringBuilder(bl.substring(a, b)).reverse()).toString());
            break;
          case "replace" : // a 文字目から b 文字目までを、指定した文字列で置き換える。
            bl.replace(a, b, order[3]);
            break;
          default :
            break;
          }
        }
      }
      // 結果を出力
      for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
      }
      
    } catch (IOException e) {
      // TODO 自動生成された catch ブロック
      e.printStackTrace();
    }
  }
}

そしたら、実行で。

f:id:ts0818:20190503155511p:plain

入力する内容は、

Input

1 行目に文字列 str が与えられます。str は英小文字のみ含みます。2 行目に命令の数 q が与えられます。続く q 行に各命令が上記の形式で与えられます。

文字列変換 | プログラミング入門 | Aizu Online Judge

ということで、 

f:id:ts0818:20190503155621p:plain

う~ん、StringBuffer、StringBuilder のどっちを使うかで迷った時は、よっぽどのことが無い限りは、StringBuilder を使っておけば良いらしいですね。

ちなみに、JavaのStringBuilderの内容をリセットするには、

qiita.com

⇧  length に、0 を設定してあげれば良いとのこと。

 

今回はこのへんで。