Ява. Объединить строку. Микро тест

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

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

}

Вышло: 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);
    }

}

Вышло: 27328.

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

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

person user471011    schedule 23.10.2011    source источник
comment
Вы запускали этот тест несколько раз, чтобы убедиться, что он правильный? У меня сложилось впечатление, что + и concat были точными эквивалентами. Хотя должна быть большая разница, если вы использовали StringBuilder...   -  person pcalcao    schedule 23.10.2011
comment
@pcalcao: они семантически эквивалентны. Никто не обещал, что у них будет такая же производительность.   -  person Donal Fellows    schedule 23.10.2011


Ответы (5)


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

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

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

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

С другой стороны, 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 предназначены для изготовления двух копий.

person NPE    schedule 23.10.2011
comment
хорошее объяснение +1. Также мы могли бы сказать, что в виртуальной машине необходимо создавать больше объектов, и я думаю, что это также причина, которая может немного замедлить работу. - person javing; 23.10.2011

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

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

person Malcolm    schedule 23.10.2011
comment
начиная с Java 1.6 это новый StringBuilder, а не StringBuffer - person Sean Patrick Floyd; 23.10.2011
comment
Спасибо за дополнение, однако ОП просто не сказал, на какой версии он работает. - person Malcolm; 24.10.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);
    }

}
person Sean Patrick Floyd    schedule 23.10.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 тоже, как это бывает, но это менее эффективно по другим причинам) выполняет интеллектуальное управление буфером за вас, это будет значительно более быстрый метод; движение еще быстрее потребует реальных усилий (или, по крайней мере, предварительного определения размера буфера, что возможно в этом случае, но в целом сложнее).

person Donal Fellows    schedule 23.10.2011

Если вы один раз пройдёте строку, такую ​​как

    String x2 = x + "x";

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

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

person Simon C    schedule 23.10.2011