StringBuffer と StringBuilder

Maverickでエンジニアをしているはぎーです。 初心者による初心者のためのJavaのお話。 今回は、今更なのですが皆大好きString[文字列結合]のお話です。

Scalaの話を見に来た方は こちら

文字列結合

文字を連結させる時ってどうやってますか?

String str = "文字列";
String str2 = "結合";
str = str + str2;
System.out.println(str);
str += str2;
System.out.println(str);
str = str.concat(str2);
System.out.println(str);
StringBuffer buffer = new StringBuffer();
buffer.append(str).append(str2);
System.out.println(buffer.toString());

※buffer.append(str + str2)としない事!

StringBuilder builder = new StringBuilder();
builder.append(str).append(str2);
System.out.println(builder.toString());

※builder.append(str + str2)としない事!

パフォーマンス

StringBufferとStringBuilderが圧勝!

\ + += concat StringBuffer StringBuilder
7050531985 6149053506 1668302159 5035470 3535256
1744332285 1767654100 1684246677 2726805 3031675
1893079563 1867336491 1949670518 1650526 2497395
1937411127 1784706081 1828080242 759785 700876
1910195345 1962878011 1775268037 1183416 1094431
1884419177 2026627222 1771173228 1151601 1001537
1810280168 1772959238 1679123452 686984 665790
1795469468 1859131489 1743807530 959268 698827
1620505270 1697486759 1703181588 807823 709243
10 1794568987 1843452735 1736388097 1035004 693362
平均 1848002118 1848410674 1735677526 1131273 1117052

※それぞれ文字列結合100000回を10セット

※単位 = ナノ秒

※平均 = 大小2位ずつを除く平均

public class StringTest {

  private static final int LOOP = 100000;

  public static long stringPlus() {
    long start;
    long stop;

    // String(+)
    String str1 = "";
    start = System.nanoTime();
    for (int i = 0; i < LOOP; i++) {
      str1 = str1 + "A";
    }
    stop = System.nanoTime();
    return stop - start;
  }

  public static long stringPlusEqual() {
    long start;
    long stop;

    // String(+=)
    String str2 = "";
    start = System.nanoTime();
    for (int i = 0; i < LOOP; i++) {
      str2 += "A";
    }
    stop = System.nanoTime();
    return stop - start;
  }

  public static long stringConcat() {
    long start;
    long stop;

    // String(concat)
    String str3 = "";
    start = System.nanoTime();
    for (int i = 0; i < LOOP; i++) {
      str3 = str3.concat("A");
    }
    stop = System.nanoTime();
    return stop - start;
  }

  public static long stringBuffer() {
    long start;
    long stop;

    // StringBuffer
    StringBuffer buffer = new StringBuffer();
    start = System.nanoTime();
    for (int i = 0; i < LOOP; i++) {
      buffer.append("A");
    }
    stop = System.nanoTime();
    return stop - start;
  }

  public static long stringBuilder() {
    long start;
    long stop;

    // StringBuilder
    StringBuilder builder = new StringBuilder();
    start = System.nanoTime();
    for (int i = 0; i < LOOP; i++) {
      builder.append("A");
    }
    stop = System.nanoTime();
    return stop - start;
  }

  public static void benchmark() {
    System.out.println("String(+):" + stringPlus());
    System.out.println("String(+=):" + stringPlusEqual());
    System.out.println("String(concat):" + stringConcat());
    System.out.println("StringBuffer:" + stringBuffer());
    System.out.println("StringBuilder:" + stringBuilder());
  }

  public static void main(String[] args){
    for (int i = 1; i <= 10; i++) {
      System.out.println(i + "セット目");
      benchmark();
    }
  }
}

StringBufferとStringBuilderの違い

【StringBuffer】

導入JDK1.0 可変文字列を扱うためのクラス スレッドセーフ

public synchronized StringBuffer append(String str) {
  super.append(str);
  return this;
}

※synchronizedがついてる

【StringBuilder】

導入JDK1.5 可変文字列を扱うためのクラス スレッドセーフではない

public StringBuilder append(String str) {
  super.append(str);
  return this;
}

※synchronizedがついてない

スレッドセーフとは(wikipedia)

まとめ

単一のスレッドにおいては+を使うよりStringBuilderを使うのが望ましいと思われます。

ただし、単一の固定文字列を一度だけ追加するような場合、私は+concatを 使用しています。コードの見やすさ、シンプルさを重要視。

以上、String[文字列結合]のお話でした。