StringBuilder#appendとStringの連結(+演算子)の続き
前に書いたStringBuilder#appendとStringの連結(+演算子) - HHeLiBeXの日記 正道編の「test1()とtest2()の処理速度が約3倍」という記述に違和感を覚えた。test1()とtest2()の差は、一時的なStringBuilderが生成されるかされないかだけであるはずだから、appendするStringオブジェクトが3個4個と増えたときに、処理速度は3倍にならないはず。
また、以下のようなコードをたまに見かけたな、ということを思い出した。
String s = "";
s += s1;
s += s2;
s += s3;
sb.append(s);
これは極端な例だが、それぞれの文はそれぞれ別々の条件判定の結果がtrueならば実行される、ということをイメージしてもらえればいいと思う。
で、前に調べたとおり、文字列の連結(+演算子)「s1 + s2」は、「new StringBuilder(s1).append(s2).toString()」という式になるのだった。
それならば、もはや容易に想像できるが‥
import java.text.DecimalFormat; import java.text.NumberFormat; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestStringBuilder { private static NumberFormat nf = new DecimalFormat("0.000"); private static int loopCount1 = 10000; private static int loopCount2 = 1000; private String s = "a"; private StringBuilder sb = new StringBuilder(loopCount2 * 2); private long start; private long end; private String name; @Before public void setUp() throws Exception { start = System.currentTimeMillis(); } @After public void tearDown() throws Exception { end = System.currentTimeMillis(); System.out.printf("%-28s %7s %7s %24s\n", name, loopCount1, loopCount2, nf.format((end - start) / 1000.0) + " sec"); } @Test public void test2_0() { test0(2); } @Test public void test2_1() { test1(2); } @Test public void test2_2() { test2(2); } @Test public void test3_0() { test0(3); } @Test public void test3_1() { test1(3); } @Test public void test3_2() { test2(3); } @Test public void test4_0() { test0(4); } @Test public void test4_1() { test1(4); } @Test public void test4_2() { test2(4); } @Test public void test5_0() { test0(5); } @Test public void test5_1() { test1(5); } @Test public void test5_2() { test2(5); } @Test public void test10_0() { test0(10); } @Test public void test10_1() { test1(10); } @Test public void test10_2() { test2(10); } @Test public void test15_0() { test0(15); } @Test public void test15_1() { test1(15); } @Test public void test15_2() { test2(15); } @Test public void test20_0() { test0(20); } @Test public void test20_1() { test1(20); } @Test public void test20_2() { test2(20); } @Test public void test25_0() { test0(25); } @Test public void test25_1() { test1(25); } @Test public void test25_2() { test2(25); } @Test public void test30_0() { test0(30); } @Test public void test30_1() { test1(30); } @Test public void test30_2() { test2(30); } private void test0(int n) { name = "test0-" + n; for (int i = 0; i < loopCount1; ++i) { sb.setLength(0); for (int j = 0; j < loopCount2; ++j) { String ss = ""; for (int k = 0; k < n; ++k) { ss += s; } sb.append(ss); } } } private void test1(int n) { name = "test1-" + n; for (int i = 0; i < loopCount1; ++i) { sb.setLength(0); for (int j = 0; j < loopCount2; ++j) { StringBuilder sb2 = new StringBuilder(); for (int k = 0; k < n; ++k) { sb2.append(s); } sb.append(sb2.toString()); } } } private void test2(int n) { name = "test2-" + n; for (int i = 0; i < loopCount1; ++i) { sb.setLength(0); for (int j = 0; j < loopCount2; ++j) { StringBuilder sb2 = sb; for (int k = 0; k < n; ++k) { sb2.append(s); } } } } }
test0()が、今回ターゲットにしている処理。
test1()は、「sb.append(s + s +‥+ s + s)」のような書き方をした場合の処理。
test2()は、「sb.append(s).append(s).‥.append(s).append(s)」のような書き方をした場合の処理。
で、結果。
test0-2 10000 1000 7.801 sec test1-2 10000 1000 4.370 sec test2-2 10000 1000 1.547 sec test0-3 10000 1000 12.612 sec test1-3 10000 1000 5.580 sec test2-3 10000 1000 2.320 sec test0-4 10000 1000 16.975 sec test1-4 10000 1000 6.054 sec test2-4 10000 1000 3.003 sec test0-5 10000 1000 22.387 sec test1-5 10000 1000 7.034 sec test2-5 10000 1000 3.725 sec test0-10 10000 1000 46.487 sec test1-10 10000 1000 10.558 sec test2-10 10000 1000 7.258 sec test0-15 10000 1000 69.924 sec test1-15 10000 1000 14.726 sec test2-15 10000 1000 10.841 sec test0-20 10000 1000 98.739 sec test1-20 10000 1000 20.117 sec test2-20 10000 1000 14.376 sec test0-25 10000 1000 125.838 sec test1-25 10000 1000 24.052 sec test2-25 10000 1000 17.923 sec test0-30 10000 1000 154.370 sec test1-30 10000 1000 27.789 sec test2-30 10000 1000 21.539 sec
最後の方はやりすぎた(ぉ
当然の結果だが、test0では連結する文字列の個数が増えれば増えるだけ、生成される一時的なStringBuilderオブジェクトが増えるので、test1()やtest2()に比べてとんでもなく時間がかかる。