Java で、文字列を連結する際には足し算ではなく StringBuilder
を使ったほうが速い、という話はよく聞くけど、そうじゃないこともあるよ。という例。
※ コメントを頂いたとおり、初出時ひどく間違っていました。すいません。初出時のコードで条件を揃えても差が出ませんでしたので、差が出るような条件に変更しました。
実行環境
$ 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
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
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);
}
}
走らせる人
run.rb
%w( UseStringBuilder UseStringPlus ).each do |name|
p name
puts( %x( javac #{name}.java && time java #{name} 10000000 ) )
end
実行結果
"UseStringBuilder"
real 0m2.594s
user 0m2.488s
sys 0m0.356s
0
"UseStringPlus"
real 0m0.227s
user 0m0.239s
sys 0m0.036s
0
サンプルコード2
コメントを受けて、コンパイル時に結合が行われないようなサンプルでの実験。
UseStringBuilder2
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
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);
}
}
走らせる人
%w( UseStringBuilder2 UseStringPlus2 ).each do |name|
p name
puts( %x( javac #{name}.java && time java #{name} 10000 a ) )
end
実行結果
"UseStringBuilder2"
real 0m1.838s
user 0m1.608s
sys 0m0.450s
0
"UseStringPlus2"
real 0m0.917s
user 0m0.903s
sys 0m0.263s
0
というわけで
というわけで、上記のようなケースでは、足し算で連結したほうが速い。
Java のバージョン等によって逆転したりとか、いろいろあると思う。