سؤال

At first step I run this code:

public class Demo  {
    public static void main(String[] args) {
        String x = "x";
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

            x = x.concat("s");

            // x+="k";

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}

Out: 13579.

On second step I run this code:

public class Demo {
    public static void main(String[] args) {
        String x = "x";
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

            //x = x.concat("s");

             x+="k";

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}

Out: 27328.

And I have two questions:

  1. Can I say that my banchmark - correct?
  2. Why so big timeline difference between (+) and concat()??? 13.5 sec VS 27 sec. Why?
هل كانت مفيدة؟

المحلول

I think that your microbenchmark is fine, and I can reproduce your result.

On my JVM, the reason x += "k" is twice as slow is that under the covers it does the following:

  1. create a new StringBuilder;
  2. append x to the StringBuilder;
  3. append "k" to the StringBuilder;
  4. call StringBuilder.toString() and assign the result to x.

This copies the character data twice (once in step 2 and once in step 4).

On the other hand, x = x.concat("s") only copies the data once.

This double copying makes x += "k" two times slower than the other version.

If you're curious, here are the bytecodes that my compiler has generated for the += loop:

   10:  goto    36
   13:  new #24; //class java/lang/StringBuilder
   16:  dup
   17:  aload_1
   18:  invokestatic    #26; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   21:  invokespecial   #32; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   24:  ldc #35; //String k
   26:  invokevirtual   #37; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   29:  invokevirtual   #41; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   32:  astore_1
   33:  iinc    4, 1
   36:  iload   4
   38:  ldc #45; //int 100000
   40:  if_icmplt   13

Instructions 21 & 29 are where the two copies are made.

نصائح أخرى

Because when you concatenate strings using +, you actually create a new StringBuffer and work with it instead of the original string, which is why this eventually may get slower than when you only use concat.

Regarding correctness, it is best to have a look at bytecode to see what is actually is going on. Compilers may optimize some code away if they know the exact result of its execution, and it never changes.

Now try this one, it will beat both of yours:

public class Demo {
    public static void main(String[] args) {
        StringBuilder x = new StringBuilder("x");
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

             x.append("k");

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}

And here's a slightly optimized version:

public class Demo {
    public static void main(String[] args) {
        StringBuilder x = new StringBuilder(100001).append('x');
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

             x.append('k');

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}

Does it help if you know that these are equivalent:

x+="k";
x = new StringBuffer(x).append("k").toString();

Or in newer Java (I forget whether it is from 1.5 or 1.6) it uses this instead, as it at least avoids the locking that StringBuffer had inflicted upon it:

x = new StringBuilder(x).append("k").toString();

With all that memory management going on, it's no wonder that the concat(String) method can do better. On the other hand, if you were going to be doing that loop, you'd be better off not converting back and forth between mutable objects and immutable ones so much:

String x = "x";
long start = System.currentTimeMillis();

StringBuilder sb = new StringBuilder(x);
for (int i = 0; i < 100000; i++) {
    sb.append("f");
}
x = sb.toString();

System.out.println(System.currentTimeMillis() - start);

Since StringBuilder (and StringBuffer too as it happens, but that's less efficient for other reasons) does intelligent buffer management for you, that's going to be a significantly faster technique; going even faster than that would require real effort (or at least pre-sizing the buffer, which is possible in this case but harder in general).

If you single-step through a line such as

    String x2 = x + "x";

in Eclipse you will see that it creates a StringBuilder object to help construct the new String. That is what is happening in your second case.

In the first case, String.concat creates a new String directly without any StringBuilder so it is a bit faster.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top