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

Учитывая 2 toString() реализации ниже, какая из них предпочтительна:

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

или

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

?

Что более важно, учитывая, что у нас есть только 3 свойства, это может не иметь значения, но в какой момент вы бы переключились с + concat на StringBuilder?

вопрос задан 7.10.2009
non sequitor
8096 репутация

19 ответов


  • 827 рейтинг

    Версия 1 предпочтительнее, потому что она короче, и компилятор фактически превратит ее в версию 2 - без разницы в производительности.

    Что более важно, учитывая, что у нас есть только 3 свойства это не может сделать разница, но в какой момент ты перейти от Конкат к строителю?

    В момент, когда вы объединяете в цикле - обычно, когда компилятор не может заменить StringBuilder сам по себе.

    ответ дан Michael Borgwardt, с репутацией 288771, 7.10.2009
  • 210 рейтинг

    Ключ в том, пишете ли вы одну конкатенацию в одном месте или накапливаете ее с течением времени.

    В приведенном вами примере нет смысла явно использовать StringBuilder. (Посмотрите на скомпилированный код для вашего первого случая. )

    Но если вы строите строку e. г. внутри цикла используйте StringBuilder.

    Чтобы уточнить, если предположить, что огромный массив содержит тысячи строк, код выглядит следующим образом:

    ...
    String result = "";
    for (String s : hugeArray) {
        result = result + s;
    }
    

    очень трата времени и памяти по сравнению с:

    ...
    StringBuilder sb = new StringBuilder();
    for (String s : hugeArray) {
        sb.append(s);
    }
    String result = sb.toString();
    
    ответ дан joel.neely, с репутацией 25739, 7.10.2009
  • 65 рейтинг

    Я предпочитаю:

    String.format( "{a: %s, b: %s, c: %s}", a, b, c );
    

    . , , потому что он короткий и читаемый.

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

    Я согласен, что если вам нужно вывести много параметров, эта форма может запутаться (как сказано в одном из комментариев). В этом случае я бы переключился на более читаемую форму (возможно, используя ToStringBuilder apache-commons - взятый из ответа Мэтта b) и снова проигнорировал бы производительность.

    ответ дан tangens, с репутацией 29209, 7.10.2009
  • 58 рейтинг

    В большинстве случаев вы не увидите реальной разницы между этими двумя подходами, но наихудший сценарий легко построить, как этот:

    public class Main
    {
        public static void main(String[] args)
        {
            long now = System.currentTimeMillis();
            slow();
            System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");
    
            now = System.currentTimeMillis();
            fast();
            System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
        }
    
        private static void fast()
        {
            StringBuilder s = new StringBuilder();
            for(int i=0;i<100000;i++)
                s.append("*");      
        }
    
        private static void slow()
        {
            String s = "";
            for(int i=0;i<100000;i++)
                s+="*";
        }
    }
    

    Выход:

    slow elapsed 11741 ms
    fast elapsed 7 ms
    

    Проблема в том, что добавление + = к строке восстанавливает новую строку, поэтому она стоит что-то линейное по отношению к длине ваших строк (сумма обоих).

    Итак - на ваш вопрос:

    Второй подход был бы быстрее, но он менее читабелен и сложнее в обслуживании. Как я уже сказал, в вашем конкретном случае вы, вероятно, не увидите разницу.

    ответ дан Omry Yadan, с репутацией 16585, 7.10.2009
  • 27 рейтинг

    У меня также был конфликт с моим боссом по поводу того, использовать ли append или +. Поскольку они используют Append (я до сих пор не могу понять, как они говорят каждый раз, когда создается новый объект). Поэтому я подумал сделать несколько R & D. Хотя я люблю объяснения Майкла Боргвардта, но просто хотел показать объяснение, если кому-то действительно понадобится знать в будущем.

    /**
     *
     * @author Perilbrain
     */
    public class Appc {
        public Appc() {
            String x = "no name";
            x += "I have Added a name" + "We May need few more names" + Appc.this;
            x.concat(x);
            // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
            //System.out.println(x);
        }
    
        public void Sb() {
            StringBuilder sbb = new StringBuilder("no name");
            sbb.append("I have Added a name");
            sbb.append("We May need few more names");
            sbb.append(Appc.this);
            sbb.append(sbb.toString());
            // System.out.println(sbb.toString());
        }
    }
    

    и разборка вышеуказанного класса выходит как

     .method public ()V //public Appc()
      .limit stack 2
      .limit locals 2
    met001_begin:                                  ; DATA XREF: met001_slot000i
      .line 12
        aload_0 ; met001_slot000
        invokespecial java/lang/Object.()V
      .line 13
        ldc "no name"
        astore_1 ; met001_slot001
      .line 14
    
    met001_7:                                      ; DATA XREF: met001_slot001i
        new java/lang/StringBuilder //1st object of SB
        dup
        invokespecial java/lang/StringBuilder.()V
        aload_1 ; met001_slot001
        invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
    g/StringBuilder;
        ldc "I have Added a nameWe May need few more names"
        invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
    g/StringBuilder;
        aload_0 ; met001_slot000
        invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
    g/StringBuilder;
        invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
        astore_1 ; met001_slot001
      .line 15
        aload_1 ; met001_slot001
        aload_1 ; met001_slot001
        invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
    g;
        pop
      .line 18
        return //no more SB created
    met001_end:                                    ; DATA XREF: met001_slot000i ...
    
    ; ===========================================================================
    
    ;met001_slot000                                ; DATA XREF: r ...
        .var 0 is this LAppc; from met001_begin to met001_end
    ;met001_slot001                                ; DATA XREF: +6w ...
        .var 1 is x Ljava/lang/String; from met001_7 to met001_end
      .end method
    ;44-1=44
    ; ---------------------------------------------------------------------------
    
    
    ; Segment type: Pure code
      .method public Sb()V //public void Sb
      .limit stack 3
      .limit locals 2
    met002_begin:                                  ; DATA XREF: met002_slot000i
      .line 21
        new java/lang/StringBuilder
        dup
        ldc "no name"
        invokespecial java/lang/StringBuilder.(Ljava/lang/String;)V
        astore_1 ; met002_slot001
      .line 22
    
    met002_10:                                     ; DATA XREF: met002_slot001i
        aload_1 ; met002_slot001
        ldc "I have Added a name"
        invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
    g/StringBuilder;
        pop
      .line 23
        aload_1 ; met002_slot001
        ldc "We May need few more names"
        invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
    g/StringBuilder;
        pop
      .line 24
        aload_1 ; met002_slot001
        aload_0 ; met002_slot000
        invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
    g/StringBuilder;
        pop
      .line 25
        aload_1 ; met002_slot001
        aload_1 ; met002_slot001
        invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
        invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
    g/StringBuilder;
        pop
      .line 28
        return
    met002_end:                                    ; DATA XREF: met002_slot000i ...
    
    
    ;met002_slot000                                ; DATA XREF: Sb+25r
        .var 0 is this LAppc; from met002_begin to met002_end
    ;met002_slot001                                ; DATA XREF: Sb+9w ...
        .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
      .end method
    ;96-49=48
    ; ---------------------------------------------------------------------------
    

    Из двух приведенных выше кодов видно, что Майкл прав. В каждом случае создается только один объект SB.

    ответ дан perilbrain, с репутацией 6301, 21.12.2011
  • 24 рейтинг

    С Java 1. 5, простая конкатенация в одну строку с "+" и StringBuilder. append () генерирует точно такой же байт-код.

    Так что для удобства чтения кода используйте «+».

    2 исключения:

    • многопоточная среда: StringBuffer
    • конкатенация в циклах: StringBuilder / StringBuffer
    ответ дан Zofren, с репутацией 693, 16.04.2012
  • 20 рейтинг

    Использование последней версии Java (1. 8) Разборка (javap -c) показывает оптимизацию, представленную компилятором. +, а sb.append() будет генерировать очень похожий код. Тем не менее, стоит проверить поведение, если мы используем + в цикле for.

    Добавление строк с помощью + в цикле for

    Java:

    public String myCatPlus(String[] vals) {
        String result = "";
        for (String val : vals) {
            result = result + val;
        }
        return result;
    }
    

    ByteCode :( отрывок цикла for)

    12: iload         5
    14: iload         4
    16: if_icmpge     51
    19: aload_3
    20: iload         5
    22: aaload
    23: astore        6
    25: new           #3                  // class java/lang/StringBuilder
    28: dup
    29: invokespecial #4                  // Method java/lang/StringBuilder."":()V
    32: aload_2
    33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    36: aload         6
    38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    44: astore_2
    45: iinc          5, 1
    48: goto          12
    

    Добавление строк с использованием stringbuilder. дописывать

    Java:

    public String myCatSb(String[] vals) {
        StringBuilder sb = new StringBuilder();
        for(String val : vals) {
            sb.append(val);
        }
        return sb.toString();
    }
    

    ByteCdoe :( отрывок цикла for)

    17: iload         5
    19: iload         4
    21: if_icmpge     43
    24: aload_3
    25: iload         5
    27: aaload
    28: astore        6
    30: aload_2
    31: aload         6
    33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    36: pop
    37: iinc          5, 1
    40: goto          17
    43: aload_2
    

    Хотя есть небольшое явное отличие . В первом случае, где использовалось +, для каждой итерации цикла создается новый StringBuilder, а сгенерированный результат сохраняется с помощью вызова toString() (с 29 по 41). Таким образом, вы генерируете промежуточные строки, которые вам действительно не нужны при использовании оператора + в цикле for.

    ответ дан ring bearer, с репутацией 14813, 26.05.2015
  • 7 рейтинг

    Из соображений производительности использование += (String) не рекомендуется. Причина в том, что Java String является неизменяемой, каждый раз, когда выполняется новая конкатенация, создается новая String (новая имеет отпечаток, отличный от старого, уже в пуле строк ). Создание новых строк оказывает давление на ГХ и замедляет программу: создание объектов стоит дорого.

    Приведенный ниже код должен сделать его более практичным и понятным одновременно.

    public static void main(String[] args) 
    {
        // warming up
        for(int i = 0; i < 100; i++)
            RandomStringUtils.randomAlphanumeric(1024);
        final StringBuilder appender = new StringBuilder();
        for(int i = 0; i < 100; i++)
            appender.append(RandomStringUtils.randomAlphanumeric(i));
    
        // testing
        for(int i = 1; i <= 10000; i*=10)
            test(i);
    }
    
    public static void test(final int howMany) 
    {
        List samples = new ArrayList<>(howMany);
        for(int i = 0; i < howMany; i++)
            samples.add(RandomStringUtils.randomAlphabetic(128));
    
        final StringBuilder builder = new StringBuilder();
        long start = System.nanoTime();
        for(String sample: samples)
            builder.append(sample);
        builder.toString();
        long elapsed = System.nanoTime() - start;
        System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);
    
        String accumulator = "";
        start = System.nanoTime();
        for(String sample: samples)
            accumulator += sample;
        elapsed = System.nanoTime() - start;
        System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);
    
        start = System.nanoTime();
        String newOne = null;
        for(String sample: samples)
            newOne = new String(sample);
        elapsed = System.nanoTime() - start;
        System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
    }
    

    Результаты для запуска приведены ниже.

    builder - 1 - elapsed: 132us
    concatenation - 1 - elapsed: 4us
    creation - 1 - elapsed: 5us
    
    builder - 10 - elapsed: 9us
    concatenation - 10 - elapsed: 26us
    creation - 10 - elapsed: 5us
    
    builder - 100 - elapsed: 77us
    concatenation - 100 - elapsed: 1669us
    creation - 100 - elapsed: 43us
    
    builder - 1000 - elapsed: 511us
    concatenation - 1000 - elapsed: 111504us
    creation - 1000 - elapsed: 282us
    
    builder - 10000 - elapsed: 3364us 
    concatenation - 10000 - elapsed: 5709793us
    creation - 10000 - elapsed: 972us
    

    Не учитывая результаты для одной конкатенации (JIT еще не выполнил свою работу), даже для 10 конкатенаций снижение производительности является значимым; для тысяч конкатенаций разница огромна.

    Уроки, извлеченные из этого очень быстрого эксперимента (легко воспроизводимого с помощью приведенного выше кода): никогда не используйте += для объединения строк вместе, даже в самых простых случаях, когда требуется несколько объединений (как сказано, создание новых строк в любом случае стоит дорого и создает давление на GC).

    ответ дан Paolo Maresca, с репутацией 4130, 21.04.2016
  • 7 рейтинг

    В Java 9 версия 1 должна быть быстрее, потому что она преобразуется в вызов invokedynamic. Более подробную информацию можно найти в JEP-280 :

    Идея состоит в том, чтобы заменить весь танец присоединения StringBuilder простым вызовом динамического вызова Java. яз. вызов. StringConcatFactory, которая будет принимать значения, требующие объединения.

    ответ дан ZhekaKozlov, с репутацией 13065, 3.04.2017
  • 4 рейтинг

    Сделайте метод toString настолько читаемым, насколько это возможно!

    Единственное исключение для этого в моей книге, если вы можете доказать мне , что он потребляет значительные ресурсы :) (Да, это означает профилирование)

    Также обратите внимание, что компилятор Java 5 генерирует более быстрый код, чем рукописный подход «StringBuffer», используемый в более ранних версиях Java. Если вы используете «+», это и будущие улучшения будут бесплатными.

    ответ дан Thorbjørn Ravn Andersen, с репутацией 56221, 7.10.2009
  • 3 рейтинг

    Кажется, есть некоторые споры о том, все еще нужно ли использовать StringBuilder с текущими компиляторами. Так что я думал, что я дам свои 2 цента опыта.

    У меня есть JDBC результирующих наборов из 10 тыс. Записей (да, мне нужны все из них в одной партии. ) Использование оператора + занимает около 5 минут на моей машине с Java 1.8. Использование stringBuilder.append("") занимает меньше секунды для того же запроса.

    Так что разница огромна. Внутри петли StringBuilder намного быстрее.

    ответ дан Eddy, с репутацией 1299, 24.04.2017
  • 2 рейтинг

    См. Пример ниже:

    //java8
    static void main(String[] args) {
        case1();//str.concat
        case2();//+=
        case3();//StringBuilder
    }
    
    static void case1() {
        List savedTimes = new ArrayList();
        long startTimeAll = System.currentTimeMillis();
        String str = "";
        for (int i = 0; i < MAX_ITERATIONS; i++) {
            long startTime = System.currentTimeMillis();
            str = str.concat(UUID.randomUUID()+"---");
            saveTime(savedTimes, startTime);
        }
        System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms");
    }
    
    static void case2() {
        List savedTimes = new ArrayList();
        long startTimeAll = System.currentTimeMillis();
        String str = "";
        for (int i = 0; i < MAX_ITERATIONS; i++) {
            long startTime = System.currentTimeMillis();
            str+=UUID.randomUUID()+"---";
            saveTime(savedTimes, startTime);
        }        
        System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms");
    }
    
    static void case3() {
        List savedTimes = new ArrayList();
        long startTimeAll = System.currentTimeMillis();
        StringBuilder str = new StringBuilder("");
        for (int i = 0; i < MAX_ITERATIONS; i++) {
            long startTime = System.currentTimeMillis();
            str.append(UUID.randomUUID()+"---");
            saveTime(savedTimes, startTime);
        }        
        System.out.println("Created string of length:"+str.length()+" in "+(System.currentTimeMillis()-startTimeAll)+" ms");
    
    }
    
    static void saveTime(List executionTimes, long startTime) {
            executionTimes.add(System.currentTimeMillis()-startTime);
            if(executionTimes.size()%CALC_AVG_EVERY == 0) {
                out.println("average time for "+executionTimes.size()+" concatenations: "+
                        NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(()->0))+
                        " ms avg");
                executionTimes.clear();
            }
    }
    

    Выход:

    среднее время на 10000 объединений: 0. 096 мс, ср.
    среднее время на 10000 объединений: 0. 185 мс, ср.
    среднее время на 10000 объединений: 0. 327 мс, ср.
    среднее время на 10000 объединений: 0. 501 мс, ср.
    среднее время на 10000 объединений: 0. 656 ms avg
    Созданная строка длины: 1950000 в 17745 мс
    среднее время на 10000 объединений: 0. 21 мс, ср.
    среднее время на 10000 объединений: 0. 652 мс, ср.
    среднее время для 10000 объединений: 1. 129 мс, ср.
    среднее время для 10000 объединений: 1. 727 мс, ср.
    среднее время на 10000 объединений: 2. 302 мс, ср.
    Созданная строка длины: 1950000 в 60279 мс
    среднее время на 10000 объединений: 0. 002 ms avg
    среднее время на 10000 объединений: 0. 002 ms avg
    среднее время на 10000 объединений: 0. 002 ms avg
    среднее время на 10000 объединений: 0. 002 ms avg
    среднее время на 10000 объединений: 0. 002 ms avg
    Созданная строка длины: 1950000 в 100 мс

    По мере увеличения длины строки увеличивается и время объединения.
    Вот где StringBuilder определенно необходим.
    Как видите, конкатенация: UUID.randomUUID()+"---", действительно не влияет на время.

    П. С. : Я не думаю, что Когда использовать StringBuilder в Java, действительно дублирует это.
    Этот вопрос говорит о toString(), который в большинстве случаев не выполняет конкатенации огромных строк.

    ответ дан Marinos An, с репутацией 2191, 31.10.2017
  • 1 рейтинг

    Мудрая производительность Конкатенация строк с использованием '+' обходится дороже, потому что она должна создавать совершенно новую копию строки, так как строки являются неизменяемыми в Java. Это играет особую роль, если конкатенация очень частая, например: внутри цикла. Вот что предлагает моя ИДЕЯ, когда я пытаюсь сделать такую ​​вещь:

    enter image description here

    Общие правила:

    • Внутри одного строкового назначения можно использовать конкатенацию строк.
    • Если вы зацикливаетесь на создании большого блока символьных данных, выберите StringBuffer.
    • Использование + = для строки всегда будет менее эффективным, чем использование StringBuffer, поэтому он должен вызывать предупреждающие сигналы - но в некоторых случаях полученная оптимизация будет незначительной по сравнению с проблемами читабельности, так что используйте свой здравый смысл.

    Вот хороший блог Джона Скита вокруг этой темы.

    ответ дан Sudip Bhandari, с репутацией 691, 24.01.2018
  • 1 рейтинг

    Могу ли я указать, что если вы собираетесь перебирать коллекцию и использовать StringBuilder, вы можете проверить Apache Commons Lang и StringUtils. join () (в разных вариантах)?

    Независимо от производительности, это избавит вас от необходимости создавать StringBuilders и для циклов за то время, которое кажется миллионным .

    ответ дан Brian Agnew, с репутацией 222977, 7.10.2009
  • 1 рейтинг

    Я сравнил четыре разных подхода для сравнения производительности. Я точно не знаю, что происходит с gc, но для меня важно время. Компилятор является важным фактором здесь. Я использовал jdk1. 8. 0_45 под окном8. 1 платформа.

    concatWithPlusOperator = 8
    concatWithBuilder = 130
    concatWithConcat = 127
    concatStringFormat = 3737
    concatWithBuilder2 = 46
    


    public class StringConcatenationBenchmark {
    
    private static final int MAX_LOOP_COUNT = 1000000;
    
    public static void main(String[] args) {
    
        int loopCount = 0;
        long t1 = System.currentTimeMillis();
        while (loopCount < MAX_LOOP_COUNT) {
            concatWithPlusOperator();
            loopCount++;
        }
        long t2 = System.currentTimeMillis();
        System.out.println("concatWithPlusOperator = " + (t2 - t1));
    
        long t3 = System.currentTimeMillis();
        loopCount = 0;
        while (loopCount < MAX_LOOP_COUNT) {
            concatWithBuilder();
            loopCount++;
        }
        long t4 = System.currentTimeMillis();
        System.out.println("concatWithBuilder = " + (t4 - t3));
    
        long t5 = System.currentTimeMillis();
        loopCount = 0;
        while (loopCount < MAX_LOOP_COUNT) {
            concatWithConcat();
            loopCount++;
        }
        long t6 = System.currentTimeMillis();
        System.out.println("concatWithConcat = " + (t6 - t5));
    
        long t7 = System.currentTimeMillis();
        loopCount = 0;
        while (loopCount < MAX_LOOP_COUNT) {
            concatStringFormat();
            loopCount++;
        }
        long t8 = System.currentTimeMillis();
        System.out.println("concatStringFormat = " + (t8 - t7));
    
        long t9 = System.currentTimeMillis();
        loopCount = 0;
        while (loopCount < MAX_LOOP_COUNT) {
            concatWithBuilder2();
            loopCount++;
        }
        long t10 = System.currentTimeMillis();
        System.out.println("concatWithBuilder2 = " + (t10 - t9));
    }
    
    private static void concatStringFormat() {
        String s = String.format("%s %s %s %s ", "String", "String", "String", "String");
    }
    
    private static void concatWithConcat() {
        String s = "String".concat("String").concat("String").concat("String");
    }
    
    private static void concatWithBuilder() {
        StringBuilder builder=new StringBuilder("String");
        builder.append("String").append("String").append("String");
        String s = builder.toString();
    }
    
    private static void concatWithBuilder2() {
        String s = new StringBuilder("String").append("String").append("String").append("String").toString();
    }
    
    private static void concatWithPlusOperator() {
        String s = "String" + "String" + "String" + "String";
    }
    }
    
    ответ дан Hamedz, с репутацией 479, 9.09.2016
  • -3 рейтинг

    Я думаю, что мы должны пойти с подходом добавления StringBuilder. Причина

    1. Конкатенация String каждый раз будет создавать новый строковый объект (так как String является неизменным объектом), поэтому он создаст 3 объекта.

    2. С помощью String Builder будет создан только один объект [StringBuilder является изменяемым], и к нему добавляется дополнительная строка.

    ответ дан VishalTambe, с репутацией 29, 24.07.2017
  • -3 рейтинг

    Для таких простых строк я предпочитаю использовать

    "string".concat("string").concat("string");
    

    В порядке, я бы сказал, что предпочтительный метод построения строки - это использование StringBuilder, String # concat (), а затем перегруженный оператор +. StringBuilder - это значительное увеличение производительности, когда работа с большими строками так же, как использование оператора + - это значительное снижение производительности (экспоненциально большое уменьшение при увеличении размера строки). Одна проблема с использованием. concat () заключается в том, что он может выдавать исключения NullPointerException.

    ответ дан Droo, с репутацией 2463, 7.10.2009