StringBuilder を使うより足し算を使ったほうが速い例(Java)

Java で、文字列を連結する際には足し算ではなく `StringBuilder` を使ったほうが速い、という話はよく聞くけど、そうじゃないこともあるよ。という例。

※ [コメント]( https://qiita.com/Nabetani/items/c58f19f694744bf26491#comment-7cb106a9c8348262c234 )を頂いたとおり、初出時ひどく間違っていました。すいません。初出時のコードで条件を揃えても差が出ませんでしたので、差が出るような条件に変更しました。

# 実行環境

```shell
$ java --version
openjdk 12 2019-03-19
OpenJDK Runtime Environment (build 12+33)
OpenJDK 64-Bit Server VM (build 12+33, mixed mode, sharing)
$ javac --version
javac 12
```
# サンプルコード1

## UseStringBuilder

```java:UseStringBuilder.java
public class UseStringBuilder {
  private static int receiver(String s) {
    if (s.equals("hoge")) {
      return 1;
    } else {
      return 0;
    }
  }

  private static final String foo = "uprjkdowvyvqxlncsjgqzstikgclvgorshfrruifnnuzcfqtgtojfqsakcfvaisaysaxbrlpfczukomfxoebwypmqbkqmeevgivpdxnawthcazjobhhhydlbeiltwbmvwsykslyudqludjqlmkzilhreuxyajqgpiwebbxdnccrfmictdxolowxxnaeoucmtybdnmujrwshdezptpjpmzjdnasikhjztfnuezvipxwvywhgvmzqtrfehkkbrgganygop";
  private static final String bar = "bjsqdbkweettnvympybihyjrkhqtedvpogounxelthhyrtbpzoldujwzmmspacjtxwjwciusocwimihuumzvarvyqorunyinmqlkaefmlghcoxsvqbbazlbgavsffmqnjyaqturfgvwrfsgdcrksdexcipznpovpzilbgpxewclexahkxylffwsjiuiuthtbkrkqimcfcfzkoptzsroljeagdzgacdpgxdqwnjymwnnvqlohvsyxkbnazgedrxdjukfh";
  private static final String baz = "phblfiycdsnaahcskasevmthowdijgvfxdzuiivsubtqdwqzcbjhxvlhclkxnmpikfphceulcypiukpkrwzrnmzabydgmfwulrkjbonpcfgvjfbewdavycsjaqfsgvuqunblbzysezasrlrkeawotzjmdvemcqfditpjuolntiymrwxbgmhupoznjydqxzhghgifwnbkgosarjtuvxgneqorxzjemqtepwldrmqytkygsfetvnclqhyxoprioatxxkwo";
  private static final String qux = "rysvvsftwtolxonofdvkjjepouyayiyszgehjuqmunagaaiacrlpalcyizpwmrscldwpfiggobbsynzczfjhcqoqmlkvobljithjucwxmyzdsbrhkjpnpezxmqegbllwcdtraovdupkhbnsekkszffesmrpigwaeqqdxtnklusiwyljbhkzhktarxcraqvcvdybpunxeqnurmrbhoptgdffcfwwemqxvivxhgdnyttkivxjfzzhbqgujigmwmetnuxod";
  private static final String quux = "aodwvgukhmcwrzxwpmlucjnnwtjviytbtbahyexaefkmmweozifczxnzxafwbyjgoknssrtrdoihwhxmktsykfgapffmzqujlgtovsikgjwjtzcuxkgmequueqlnjratiqrznxpyomvsyiccbiyqrwbelbhpvvpgscqzpdsnqrohphdiomjcwivxddpyqelcdnapzlixbqdsfrehamzhyaavpuxjklllvulfqyostchdrgjgdbugfoeuknbsbrntkevf";

  public static void main(String[] args) {
    int sum = 0;
    String[] bars = new String[] { "baz", "qux" };
    for (int i = 0; i < Integer.parseInt(args[0]); ++i) {
      sum += receiver(new StringBuilder()
      .append("hoge")
      .append( foo )
      .append( bar )
      .append( baz )
      .append( qux )
      .append( quux )
      .toString() );
    }
    System.out.println(sum);
  }
}
```

## UseStringPlus

```java:UseStringPlus.java
public class UseStringPlus {
  private static int receiver( String s ){
    if ( s.equals("hoge") ){
      return 1;
    } else {
      return 0;
    }
  }
  private static final String foo = "uprjkdowvyvqxlncsjgqzstikgclvgorshfrruifnnuzcfqtgtojfqsakcfvaisaysaxbrlpfczukomfxoebwypmqbkqmeevgivpdxnawthcazjobhhhydlbeiltwbmvwsykslyudqludjqlmkzilhreuxyajqgpiwebbxdnccrfmictdxolowxxnaeoucmtybdnmujrwshdezptpjpmzjdnasikhjztfnuezvipxwvywhgvmzqtrfehkkbrgganygop";
  private static final String bar = "bjsqdbkweettnvympybihyjrkhqtedvpogounxelthhyrtbpzoldujwzmmspacjtxwjwciusocwimihuumzvarvyqorunyinmqlkaefmlghcoxsvqbbazlbgavsffmqnjyaqturfgvwrfsgdcrksdexcipznpovpzilbgpxewclexahkxylffwsjiuiuthtbkrkqimcfcfzkoptzsroljeagdzgacdpgxdqwnjymwnnvqlohvsyxkbnazgedrxdjukfh";
  private static final String baz = "phblfiycdsnaahcskasevmthowdijgvfxdzuiivsubtqdwqzcbjhxvlhclkxnmpikfphceulcypiukpkrwzrnmzabydgmfwulrkjbonpcfgvjfbewdavycsjaqfsgvuqunblbzysezasrlrkeawotzjmdvemcqfditpjuolntiymrwxbgmhupoznjydqxzhghgifwnbkgosarjtuvxgneqorxzjemqtepwldrmqytkygsfetvnclqhyxoprioatxxkwo";
  private static final String qux = "rysvvsftwtolxonofdvkjjepouyayiyszgehjuqmunagaaiacrlpalcyizpwmrscldwpfiggobbsynzczfjhcqoqmlkvobljithjucwxmyzdsbrhkjpnpezxmqegbllwcdtraovdupkhbnsekkszffesmrpigwaeqqdxtnklusiwyljbhkzhktarxcraqvcvdybpunxeqnurmrbhoptgdffcfwwemqxvivxhgdnyttkivxjfzzhbqgujigmwmetnuxod";
  private static final String quux = "aodwvgukhmcwrzxwpmlucjnnwtjviytbtbahyexaefkmmweozifczxnzxafwbyjgoknssrtrdoihwhxmktsykfgapffmzqujlgtovsikgjwjtzcuxkgmequueqlnjratiqrznxpyomvsyiccbiyqrwbelbhpvvpgscqzpdsnqrohphdiomjcwivxddpyqelcdnapzlixbqdsfrehamzhyaavpuxjklllvulfqyostchdrgjgdbugfoeuknbsbrntkevf";
  public static void main( String[] args ){
    int sum=0;
    String[] bars = new String[]{ "baz", "qux" };
    for( int i=0 ; i<Integer.parseInt(args[0]) ; ++i ){
      sum += receiver( 
        "hoge"
        + foo 
        + bar 
        + baz 
        + qux 
        + quux 
       );
    }
    System.out.println(sum);
  }
}
```

## 走らせる人

```ruby:run.rb
%w( UseStringBuilder UseStringPlus ).each do |name|
  p name
  puts( %x( javac #{name}.java && time java #{name} 10000000 ) )
end
```

## 実行結果

```text
"UseStringBuilder"

real	0m2.594s
user	0m2.488s
sys	0m0.356s
0
"UseStringPlus"

real	0m0.227s
user	0m0.239s
sys	0m0.036s
0
```

# サンプルコード2

[コメント]( https://qiita.com/Nabetani/items/c58f19f694744bf26491#comment-2fd27c0ed987c0e88cbc )を受けて、コンパイル時に結合が行われないようなサンプルでの実験。

## UseStringBuilder2

```java:UseStringBuilder2.java
public class UseStringBuilder2 {
  private static int receiver(String s) {
    if (s.equals("hoge")) {
      return 1;
    } else {
      return 0;
    }
  }

  private static String makestr(int len, char c0){
    StringBuilder b = new StringBuilder();
    for( int i=0 ; i<len*1024 ; ++i ){
      b.append(c0+(char)i);
    }
    return b.toString();
  }

  public static void main(String[] args) {
    String foo = makestr(1, args[1].charAt(0));
    String bar = makestr(2, args[1].charAt(0));
    String baz = makestr(4, args[1].charAt(0));
    String qux = makestr(8, args[1].charAt(0));
    String quux = makestr(16, args[1].charAt(0));
    String corge = makestr(32, args[1].charAt(0));
    int sum = 0;
    String[] bars = new String[] { "baz", "qux" };
    for (int i = 0; i < Integer.parseInt(args[0]); ++i) {
      sum += receiver(new StringBuilder()
      .append("hoge")
      .append( foo )
      .append( bar )
      .append( baz )
      .append( qux )
      .append( quux )
      .append( corge )
      .toString() );
    }
    System.out.println(sum);
  }
}
```

## UseStringPlus2

```java:UseStringPlus2.java
public class UseStringPlus2 {
  private static int receiver( String s ){
    if ( s.equals("hoge") ){
      return 1;
    } else {
      return 0;
    }
  }
  private static String makestr(int len, char c0){
    StringBuilder b = new StringBuilder();
    for( int i=0 ; i<len*1024 ; ++i ){
      b.append(c0+(char)i);
    }
    return b.toString();
  }
  public static void main( String[] args ){
    String foo = makestr(1, args[1].charAt(0));
    String bar = makestr(2, args[1].charAt(0));
    String baz = makestr(4, args[1].charAt(0));
    String qux = makestr(8, args[1].charAt(0));
    String quux = makestr(16, args[1].charAt(0));
    String corge = makestr(32, args[1].charAt(0));
    int sum=0;
    for( int i=0 ; i<Integer.parseInt(args[0]) ; ++i ){
      sum += receiver( 
        "hoge"
        + foo 
        + bar 
        + baz 
        + qux 
        + quux 
        + corge
       );
    }
    System.out.println(sum);
  }
}
```

## 走らせる人

```ruby
%w( UseStringBuilder2 UseStringPlus2 ).each do |name|
  p name
  puts( %x( javac #{name}.java && time java #{name} 10000 a ) )
end
```

## 実行結果

```text
"UseStringBuilder2"

real	0m1.838s
user	0m1.608s
sys	0m0.450s
0
"UseStringPlus2"

real	0m0.917s
user	0m0.903s
sys	0m0.263s
0
```

# というわけで

というわけで、上記のようなケースでは、足し算で連結したほうが速い。

Java のバージョン等によって逆転したりとか、いろいろあると思う。