HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

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()に比べてとんでもなく時間がかかる。