Джава.Конкатенация строкМикро Бенчмарк - PullRequest
3 голосов
/ 23 октября 2011

На первом шаге я запускаю этот код:

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.

На втором шаге я запускаю этот код:

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.

И у меня есть два вопроса:

  1. Могу ли я сказать, что мой бенчмарк - правильный?
  2. Почему такая большая разница во времени между (+) и concat () ??? 13,5 сек. VS 27 сек. Почему?

Ответы [ 5 ]

6 голосов
/ 23 октября 2011

Я думаю, что с вашим микробенчмарком все в порядке, и я могу воспроизвести ваш результат.

На моей JVM причина x += "k" в два раза медленнее заключается в том, что под крышками выполняется следующее:

  1. создать новый StringBuilder;
  2. добавить x к StringBuilder;
  3. добавить "k" к StringBuilder;
  4. позвоните StringBuilder.toString() и присвойте результат x.

. Копирует данные символов дважды (один раз на шаге 2 и один раз на шаге 4).

С другой стороныhand, x = x.concat("s") копирует данные только один раз.

Это двойное копирование делает x += "k" в два раза медленнее, чем в другой версии.

Если вам интересно, вот байт-коды, которые мойкомпилятор сгенерировал для цикла +=:

   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

В инструкциях 21 и 29 сделаны две копии.

0 голосов
/ 23 октября 2011

Если вы проходите по одной строке, например

    String x2 = x + "x";

в Eclipse вы увидите, что он создает StringBuilder объект, чтобы помочь построить новый String. Вот что происходит в вашем втором случае.

В первом случае String.concat создает новый String напрямую без любой StringBuilder, так что это немного быстрее.

0 голосов
/ 23 октября 2011

Помогает ли это, если вы знаете, что они эквивалентны:

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

Или в более новой Java (я забыл, от 1.5 или 1.6) он использует это вместо этого, поскольку он по крайней мере избегает блокировки, которую StringBuffer наложил на него:

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

При всем этом управлении памятью неудивительно, что метод concat(String) может работать лучше. С другой стороны, если вы собираетесь делать этот цикл, вам лучше не конвертировать туда и обратно между изменяемыми объектами и неизменяемыми:

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);

Поскольку StringBuilderStringBuffer тоже, как это бывает, но по другим причинам это менее эффективно) выполняет интеллектуальное управление буфером для вас, это будет значительно более быстрый метод; Чтобы работать даже быстрее, это потребовало бы реальных усилий (или, по крайней мере, предварительного определения размера буфера, что в данном случае возможно, но в целом сложнее).

0 голосов
/ 23 октября 2011

Теперь попробуйте это, оно победит вас обоих:

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);
    }

}

А вот немного оптимизированная версия:

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);
    }

}
0 голосов
/ 23 октября 2011

Потому что, когда вы объединяете строки, используя +, вы фактически создаете новый StringBuffer и работаете с ним вместо исходной строки, поэтому в конечном итоге это может оказаться медленнее, чем при использовании только concat.

Что касается правильности, лучше всего взглянуть на байт-код, чтобы увидеть, что на самом деле происходит.Компиляторы могут оптимизировать некоторый код, если он знает точный результат его выполнения, и он никогда не изменяется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...