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


926

Учитывая 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?


40
В какой момент вы переключаетесь на StringBuilder? Когда это влияет на память или производительность. Или когда это могло бы. Если вы действительно делаете это только для пары строк, не беспокойтесь. Но если вы собираетесь делать это снова и снова, вы должны увидеть ощутимую разницу при использовании StringBuilder.
ewall

что означает 100 в параметре?
Асиф Муштак

2
@UnKnown 100 - начальный размер
StringBuilder

@nonsequitor Значит, максимум символов будет 100?
Асиф Муштак

10
@ Неизвестно не только начальный размер, если вы знаете приблизительный размер строки, с которой вы имеете дело, то вы можете сказать, StringBuilderкакой размер выделять авансом, в противном случае, если ему не хватит места, придется удвоить размер, создав Новый char[]массив затем скопировать данные поверх - что дорого. Вы можете обмануть, указав размер, и тогда нет необходимости в создании этого массива - поэтому, если вы считаете, что ваша строка будет длиной ~ 100 символов, вы можете установить StringBuilder на этот размер, и он никогда не будет расширяться внутри.
не секвестор

Ответы:


964

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

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

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


19
Это правда, но в справочнике по языку также говорится, что это необязательно. Фактически, я только что провел простой тест с JRE 1.6.0_15 и не увидел никакой оптимизации компилятора в декомпилированном классе.
Бруно Конде

37
Я только что попробовал код из вопроса (скомпилированный на JDK 1.6.0_16) и нашел оптимизацию, как и ожидалось. Я уверен, что все современные компиляторы это сделают.
Майкл Боргвардт

22
Ты прав. Глядя на байт-код, я ясно вижу оптимизацию StringBuilder. Я использовал декомпилятор и, кое-как, он конвертирует обратно в concat. +1
Бруно Конде

80
Не бить мертвого коня, но формулировка в спецификации: To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.Ключевое слово там может быть . Учитывая, что это официально необязательно (хотя, скорее всего, реализовано), не должны ли мы защитить себя?
Лукас

93
@ Лукас: Нет, мы не должны. Если компилятор решит не выполнять эту оптимизацию, это произойдет потому, что оно того не стоит. В 99% случаев компилятор лучше знает, какая оптимизация того стоит, поэтому, как правило, разработчик не должен вмешиваться. Конечно, ваша ситуация может попасть в другие 1%, но это может быть проверено только (тщательным) сравнительным тестированием.
слеске

255

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

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

Но если вы строите строку, например, внутри цикла, используйте 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();

3
Да, StringBuilder не нужно заново создавать объект String снова и снова.
Ольга

124
Черт возьми, я использовал эти две функции для проверки большой строки, в которой я работаю. 6.51мин против 11 сек
user1722791

1
Кстати, вы также можете использовать result += s;(в первом примере)
RAnders00

1
сколько объектов будет создано этим утверждением? "{a:"+ a + ", b:" + b + ", c: " + c +"}";
Асиф Муштак

1
Как насчет чего-то вроде: String str = (a == null)? null: a '+ (b == null)? null: b '+ (c == null)? с: с '+ ...; ? Помешает ли это оптимизации?
amitfr

75

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

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

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

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

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


65
Это на самом деле длиннее, содержит больше символов и имеет переменные текст из последовательности.
Том Хотин - tackline

4
Так вы бы сказали, что он менее читабелен, чем один из других подходов?
тангенс

3
Я предпочитаю писать это, потому что проще добавлять больше переменных, но я не уверен, что это более читабельно - особенно, когда количество аргументов становится большим. Это также не работает в те моменты, когда вам нужно добавлять биты в разное время.
Алекс Фейнман

78
Кажется, труднее читать (для меня). Теперь я должен сканировать назад и вперед между {...} и параметрами.
Стив Куо

10
Я предпочитаю эту форму, потому что это безопасно, если один из параметровnull
rds

74

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

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

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

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

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


Не забывайте о .concat (). Я бы предположил, что истекшее время может быть где-то от 10 до 18 мс, что делает его незначительным при использовании коротких строк, как в оригинальном пост-примере.
Droo

9
Хотя вы правы +=, оригинальный пример представлял собой последовательность +, которую компилятор преобразует в один string.concatвызов. Ваши результаты не применяются.
Blindy

1
@Blindy & Droo: - Вы оба правы. Использование .concate в таком сценарии - лучший обходной путь, так как + = создает новый объект каждый раз, когда выполняется процедура цикла.
Perilbrain

3
Вы знаете, что его toString () не вызывается в цикле?
Омри Ядан

Я попробовал этот пример, чтобы проверить скорость. Итак, мои результаты: медленно прошло 29672 мс; быстро прошло 15 мс Так что ответ очевиден. Но если это будет 100 итераций - время одинаковое - 0 мс. Если 500 итераций - 16 мс и 0 мс. И так далее.
Эрнестас Груодис

28

Я также столкнулся с моим боссом по поводу того, использовать ли 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 <init>()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.<init>()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.<init>()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: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+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.<init>(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.


27

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

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

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

  • многопоточная среда: StringBuffer
  • конкатенация в циклах: StringBuilder / StringBuffer

22

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

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

Ява:

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."<init>":()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.append

Ява:

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цикле.


1
Это Oracle JDK или OpenJDK?
Кристоф Русси

12

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

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


9

Из соображений производительности использование +=( 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<String> 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

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

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


9

Смотрите пример ниже:

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> 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() {
    System.out.println("[str1+=str2]");
    List<Long> 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() {
    System.out.println("[str1.append(str2)]");
    List<Long> 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<Long> 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();
    }
}

Вывод:

версия байт-кода java: 8
java.version: 1.8.0_144
[str1.concat (str2)]
среднее время для 10000 конкатенаций: 0,096 мс,
среднее время для 10000 конкатенаций: 0,185 мс,
среднее время для 10000 конкатенаций: 0,327 мс,
среднее время для 10000 объединений: 0,501 мсек.
Среднее время для 10000 объединений: 0,656 мсек. Ср.
Созданная строка длины: 1950000 за 17745 мс. Созданная строка длины: 1950000 за 60279 мс. [Str1.append (str2)] среднее время для 10000 объединений: 0,002 мсек. среднее время для 10000 объединений: 0,002 мсек. среднее время для 10000 объединений: 0,002 мсек.
[str1 + = str2]
среднее время для 10000 объединений: 0,21 мс,
среднее время для 10000 объединений: 0,652 мс
среднее время для 10000 объединений: 1,129 мсек.
Среднее время для 10000 объединений: 1,727 мсек.
среднее время для 10000 объединений: 2.302 мс. avg.





среднее время для 10000 объединений: 0,002 мсек.
среднее время для 10000 объединений: 0,002 мсек. avg
Созданная строка длины: 1950000 в 100 мсек.

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

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


2019 Обновление

С тех java8пор все немного изменилось. Кажется, что сейчас (java13) время конкатенации +=практически такое же, как str.concat(). Однако StringBuilderвремя объединения остается неизменным . (Оригинальный пост выше был немного отредактирован, чтобы добавить более подробный вывод)

версия байт-кода java: 13
java.version: 13.0.1
[str1.concat (str2)]
среднее время для 10000 конкатенаций: 0,047 мс,
среднее время для 10000 конкатенаций: 0,1 мс,
среднее время для 10000 конкатенаций: 0,17 мс,
среднее время для 10000 объединений: 0,255 мс,
среднее время для 10000 объединений: 0,336 мс, avg
Созданная строка длины: 1950000 за 9147 мс
[str1 + = str2]
среднее время для 10000 объединений: 0,037 мс
среднее время для 10000 объединений: 0,097 мс
среднее время для 10000 объединений: 0,249 мсек. Среднее время для 10000 объединений: 0,326 мсек.
Среднее время для 10000 объединений: 0,298 мсек.

Созданная строка длины: 1950000 за 10191 мс
[str1.append (str2)]
среднее время для 10000 конкатенаций: 0,001 мс,
среднее время для 10000 конкатенаций: 0,001 мс,
среднее время для 10000 конкатенаций: 0,001 мс,
среднее время для 10000 конкатенаций:
Среднее время усреднения 0,001 мсек для 10000 объединений
: среднеквадратичное значение 0,001 мсек. Длина строки: 1950000 в 43 мсек.

Стоит отметить, что bytecode:8/java.version:13комбинация имеет хороший выигрыш в производительности по сравнению сbytecode:8/java.version:8


Это должен быть принятый ответ .. это зависит от размера String Stream, который определяет выбор concat или
StringBuilder

@ user1428716 FYI: ответ обновлен с результатами, java13которые теперь отличаются. Но я думаю, что главный вывод остается прежним.
Marinos

7

Apache Commons-Lang имеет класс ToStringBuilder, который очень прост в использовании. Он отлично справляется как с логикой добавления, так и с форматированием того, как вы хотите, чтобы ваша строка была похожа.

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

Вернет вывод, который выглядит как com.blah.YourClass@abc1321f[a=whatever, b=foo].

Или в более сжатой форме, используя цепочку:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

Или, если вы хотите использовать отражение, чтобы включить каждое поле класса:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

Вы также можете настроить стиль ToString, если хотите.


4

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

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

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


3

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

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

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


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

2

С точки зрения производительности конкатенация строк с использованием «+» обходится дороже, потому что она должна создавать совершенно новую копию строки, поскольку строки неизменяемы в Java. Это играет особую роль, если конкатенация очень частая, например: внутри цикла. Вот что предлагает моя IDEA, когда я пытаюсь сделать такую ​​вещь:

введите описание изображения здесь

Основные правила:

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

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


2
Вы никогда не должны использовать StringBuffer, если вам абсолютно не требуется синхронизированный доступ из нескольких потоков. В противном случае предпочтение отдается StringBuilder, который не синхронизирован и поэтому имеет меньше накладных расходов.
Эрки дер Луни

1

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

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


1

Вот что я проверял в Java8

  • Использование конкатенации строк
  • Использование StringBuilder

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

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

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms

-1

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

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

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


Почему этот ответ опущен? docs.oracle.com/javase/8/docs/api/java/util/stream/… - изменяемое сокращение
user1428716

-4

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

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

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


9
Использование concat () может работать хуже, чем «+», поскольку JLS позволяет конвертировать «+» в StringBuilder, и, скорее всего, все JVM делают это или используют более эффективную альтернативу - то же самое вряд ли будет верно для concat, который должен создать и отбросить хотя бы одну полную промежуточную строку в вашем примере.
Лоуренс Дол
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.