Как Java выполняет конкатенацию строк, используя "+"? - PullRequest
9 голосов
/ 27 апреля 2010

Я читал о том, как Java работает с оператором +=, используя StringBuilder.
То же самое с операцией ("a" + "b")?

Ответы [ 6 ]

36 голосов
/ 27 апреля 2010

Если объединить строки literal (буквально "foo" + "bar"), компилятор сделает это во время компиляции, а не во время выполнения.

Если у вас есть две не-литеральные строки и соедините их с +, компилятор (в любом случае, Sun) будет использовать StringBuilder под обложками, но не обязательно самым эффективным способом. Так, например, если у вас есть это:

String repeat(String a, int count) {
    String rv;

    if (count <= 0) {
        return "";
    }

    rv = a;
    while (--count > 0) {
        rv += a;
    }
    return rv;
}

... что на самом деле будет генерировать компилятор Sun, когда байт-код будет выглядеть что-то примерно так:

String repeat(String a, int count) {
    String rv;

    if (count <= 0) {
        return "";
    }

    rv = a;
    while (--count > 0) {
        rv = new StringBuilder().append(rv).append(a).toString();
    }
    return rv;
}

(Да, действительно - см. Разборку в конце этого ответа.) Обратите внимание, что он создавал новый StringBuilder на каждой итерации, а затем преобразовывал результат в String. Это неэффективно (но это не имеет значения, если вы не делаете это lot ) из-за всех временных выделений памяти: оно выделяет StringBuilder и его буфер, вполне возможно, перераспределяет буфер на первый append [если rv имеет длину более 16 символов, что является размером буфера по умолчанию], и если не в первом, то почти наверняка во втором append, в конце выделяется String в конце & mdash ; и затем делает это все снова на следующей итерации.

При необходимости вы можете повысить эффективность, переписав его, чтобы явно использовать StringBuilder:

String repeat(String a, int count) {
    StringBuilder rv;

    if (count <= 0) {
        return "";
    }

    rv = new StringBuilder(a.length() * count);
    while (count-- > 0) {
        rv.append(a);
    }
    return rv.toString();
}

Там мы использовали явный StringBuilder, а также установили его начальную емкость буфера достаточно большой, чтобы вместить результат. Это более эффективно использует память, но, конечно, немного менее понятно неопытным разработчикам кода и немного труднее писать. Так что , если вы обнаружите проблему с производительностью в тесном цикле конкатата строки, это может быть способ ее решения.

Вы можете увидеть это под прикрытием StringBuilder в действии со следующим тестовым классом:

public class SBTest
{
    public static final void main(String[] params)
    {
        System.out.println(new SBTest().repeat("testing ", 4));
        System.exit(0);
    }

    String repeat(String a, int count) {
        String rv;

        if (count <= 0) {
            return "";
        }

        rv = a;
        while (--count > 0) {
            rv += a;
        }
        return rv;
    }
}

... который разбирает (используя javap -c SBTest) вот так:

Compiled from "SBTest.java"
public class SBTest extends java.lang.Object{
public SBTest();
Code:
   0: aload_0
   1: invokespecial  #1; //Method java/lang/Object."<init>":()V
   4: return

public static final void main(java.lang.String[]);
Code:
   0: getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3: new   #3; //class SBTest
   6: dup
   7: invokespecial  #4; //Method "<init>":()V
   10: ldc   #5; //String testing
   12: iconst_4
   13: invokevirtual  #6; //Method repeat:(Ljava/lang/String;I)Ljava/lang/String;
   16: invokevirtual  #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   19: iconst_0
   20: invokestatic   #8; //Method java/lang/System.exit:(I)V
   23: return

java.lang.String repeat(java.lang.String, int);
Code:
   0: iload_2
   1: ifgt  7
   4: ldc   #9; //String
   6: areturn
   7: aload_1
   8: astore_3
   9: iinc  2, -1
   12: iload_2
   13: ifle  38
   16: new   #10; //class java/lang/StringBuilder
   19: dup
   20: invokespecial  #11; //Method java/lang/StringBuilder."<init>":()V
   23: aload_3
   24: invokevirtual  #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   27: aload_1
   28: invokevirtual  #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   31: invokevirtual  #13; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   34: astore_3
   35: goto  9
   38: aload_3
   39: areturn

}

Обратите внимание, как новый StringBuilder создается на каждой итерации цикла и создается с использованием емкости буфера по умолчанию.

Весь этот материал временного размещения звучит уродливо, но опять же, только если вы имеете дело с существенными циклами и / или существенными строками. Кроме того, когда результирующий байт-код запускается, JVM вполне может оптимизировать его дальше. Например, Sun HotSpot JVM - это очень зрелый JIT-оптимизирующий компилятор. Как только он определил петлю как горячую точку, он вполне может найти способ ее рефакторинга. Или нет, конечно. : -)

Мое эмпирическое правило: я беспокоюсь об этом, когда вижу проблему с производительностью, или если я знаю, что выполняю много конкатенации, и очень вероятно, что 1056 * будет проблема с производительностью, и код не окажет значительного влияния с точки зрения обслуживания, если я вместо этого использую StringBuilder. Бешеная лига против преждевременной оптимизации, вероятно, не согласилась бы со мной на втором из них. : -)

14 голосов
/ 27 апреля 2010

Нет. Использование StringBuilder не то же самое, что "a" + "b".

В Java экземпляры String являются неизменяемыми.

Итак, если вы сделаете:

String c = "a" + "b";

Вы создаете новые строки каждый раз, когда объединяете.

С другой стороны, StringBuilder похож на буфер, который может увеличиваться по мере необходимости при добавлении новых строк.

StringBuilder c = new StringBuilder();
c.append("a");
c.append("b"); // c is only created once and appended "a" and "b".

Правило большого пальца (изменено благодаря полученным комментариям):

Если вы собираетесь много конкатенировать (то есть конкатенировать в цикле или генерировать большой XML, образованный несколькими строковыми конкатенированными переменными), используйте StringBuilder. В противном случае, простая конкатенация (с использованием оператора +) будет очень хорошо.

Оптимизация компилятора также играет огромную роль при компиляции такого рода кода.

Вот дальнейшее объяснение по теме.

И еще вопросы StackOVerflow по этому вопросу:

Лучше ли повторно использовать StringBuilder в цикле?

Каков наилучший способ создания строки элементов с разделителями в Java?

StringBuilder против конкатенации строк в toString () в Java

5 голосов
/ 27 апреля 2010

Да, то же самое, но компилятор может дополнительно оптимизировать конкатенации литералов перед выдачей кода, поэтому "a"+"b" можно просто выдать как "ab" напрямую.

4 голосов
/ 27 апреля 2010

Для объединения фиксированного числа строк в одном выражении с + компилятор выдаст код, используя один StringBuilder.

например. линия

String d = a + b + c;

приводит к тому же байт-коду, что и строка

String d = new StringBuilder().append(a).append(b).append(c).toString();

при компиляции с использованием компилятора javac. (Компилятор Eclipse создает несколько более оптимизированный код, вызывая new StringBuilder(a), сохраняя, таким образом, один вызов метода.)

Как уже упоминалось в других ответах, компилятор объединяет строковые литералы, такие как "a" + "b", в одну строку, создавая байт-код, содержащий взамен "ab".

Как уже упоминалось в сети, вы не должны использовать + для создания одной строки в цикле , потому что вы копируете начало строки снова и снова в новые строки. В этой ситуации вы должны использовать один StringBuilder, который вы объявляете вне цикла.

0 голосов
/ 27 апреля 2010

"a" + "b" операция

Несмотря на читаемость, простоту форматирования и простоту, конкатенация строк с "+" считается плохой в Java.

Каждый раз, когда вы добавляете что-либо с помощью «+» (String.concat ()), создается новая строка, содержимое старой строки копируется, добавляется новое содержимое, а старая строка отбрасывается. Чем больше значение String, тем больше времени требуется - чем больше копировать, тем больше мусора. Примечание: если вы просто объединяете несколько (скажем, 3,4) строк и не строите строку с помощью цикла, или просто пишете какое-то тестовое приложение, вы все равно можете придерживаться "+"

Использование StringBuilder

При выполнении обширных манипуляций со строками (или добавление через цикл), вероятно, рекомендуется заменить «+» на StringBuilder .append. Промежуточные объекты, упомянутые в случае «+», не создаются при вызове метода append().

Также следует отметить, что оптимизации в компиляторе Sun Java, который автоматически создает StringBuilders (StringBuffers <5.0), когда он видит конкатенации строк. Но это всего лишь Sun Java компилятор. </p>

0 голосов
/ 27 апреля 2010

Строки чаще объединяются с оператором +, как в "Hello," + " world" + "!"

Источник

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