Почему не рекомендуются множественные конкатенации string

от admin

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

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

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

100 символов, вы можете установить StringBuilder на этот размер, и ему никогда не придется расширяться внутри.

20 ответов

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

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

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

500 итераций за 20 секунд до 35 000 итераций за 5 секунд. Я был просто в шоке от разницы. Были также некоторые конкатенации целых чисел, которые я заменил вызовами String.format, что, вероятно, повысило производительность; честно говоря, я не уверен, какое изменение оказало наибольшее влияние. Но полагать, что «+» всегда просто отлично, — ошибка.

+ быстрее, чем Java 9, если только JVM не знает, как его оптимизировать (например, конкатенация в цикле).

Я проверил байт-код для следующего кода (в Java 17):

Версия + просто вызывает динамическую функцию makeConcatWithConstants и передает аргумент метода ( \u0001 является заполнителем параметра).
В то время как версия StringBuilder должна делать это «честным» способом.
Я думаю, теперь мы можем понять, почему + быстрее.

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

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

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

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

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

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

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

Начиная с Java 1.5, простое объединение одной строки с помощью «+» и StringBuilder.append () генерирует точно такой же байт-код.

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

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

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

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

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

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

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

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

Добавление строк с помощью stringbuilder.append

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

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

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

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

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

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

Результаты пробега представлены ниже.

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

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

Это зависит от размера строки.

версия байт-кода 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 мс
[str1 + = str2]
среднее время для 10000 конкатенаций: 0,21 мс в среднем
среднее время для 10000 конкатенаций: 0,652 мс в среднем
среднее время для 10000 конкатенаций: 1,129 мс в среднем
среднее время для 10000 конкатенаций: 1,727 мс в среднем
среднее время для 10000 конкатенаций: 2,302 мс в среднем
Созданная строка длиной: 1950000 в 60279 мс
[str1.append (str2)]
среднее время для 10000 конкатенаций: 0,002 мс в среднем
среднее время для 10000 конкатенаций: 0,002 мс в среднем
среднее время для 10000 конкатенаций: 0,002 мс в среднем
среднее время для 10000 конкатенаций: 0,002 мс в среднем
среднее время для 10000 конкатенаций: 0,002 мс в среднем
Созданная строка длиной: 1950000 в 100 мс

По мере увеличения длины строки увеличивается время конкатенации += и .concat , причем последнее является более эффективным, но все же непостоянным
Вот где StringBuilder определенно нужен.

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

JDK 9/JEP 280: конкатенация строк никогда больше не будет прежней

И снова здравствуйте. Как мы уже писали, на следующей неделе стартует новая группа обучения по курсу «Разработчик Java», по устоявшейся традиции делимся с вами переводом интересного материала по теме.

Начиная с JDK 9 конкатенация строк претерпела значительные изменения.

JEP 280 («Indify String Concatenation») был реализован в рамках JDK 9 и, в соответствии с разделом «Summary»: «Изменяет статическую последовательность байт-кода конкатенации строк, сгенерированную javac, для использования вызовов invokedynamic к функциям библиотеки JDK». Влияние, которое это оказывает на конкатенацию строк в Java, легче всего заметить, посмотрев на javap-вывод классов, использующих конкатенацию строк, которые скомпилированы в JDK до JDK 9 и после JDK 9.

Читать:
Windows update health tools что это

/>

Для первой демонстрации будет использоваться простой Java-класс с именем «HelloWorldStringConcat».

Ниже показано сопоставление различий для -verbose вывода javap для метода main(String) класса HelloWorldStringConcat при компиляции с JDK 8 (AdoptOpenJDK) и JDK 11 (Oracle OpenJDK). Я выделил несколько ключевых различий.

JDK 8 javap-вывод

JDK 11 javap-вывод

Раздел «Description» в JEP 280 описывает это различие: «Идея состоит в том, чтобы заменить весь танец присоединения StringBuilder простым вызовом invokedynamic к java.lang.invoke.StringConcatFactory, который будет принимать значения, требующие объединения». В этом же разделе показано аналогичное сравнение скомпилированного вывода для аналогичного примера конкатенации строк.

Скомпилированный вывод с JDK 11 для простой конкатенации — это не просто меньшее количество строк, чем в выводе с JDK 8; у него также меньше «дорогих» операций. Потенциальное улучшение производительности может быть достигнуто за счет того, что нет необходимости в обертывании примитивных типов и не требуется создавать множество дополнительных объектов. Одним из основных мотивов этого изменения было «заложить основу для создания оптимизированных обработчиков конкатенации строк, реализуемых без необходимости изменения компилятора Java-to-bytecode» и «включить будущие оптимизации конкатенации строк без дополнительных изменений в байт-коде генерируемом javac. «

Есть интересное следствие этого с точки зрения использования StringBuffer (которому мне в любом случае сложно найти хорошее применение) и StringBuilder. В JEP 280 в “Non-Goal” было заявлено не «вводить какие-либо новые API-интерфейсы для String и/или StringBuilder, которые могли бы помочь в создании более эффективных стратегий перевода». В связи с этим для простой конкатенации строк, такой как в примере в начале этого поста, явное использование StringBuilder и StringBuffer фактически исключает для компилятора возможность использовать фичу, представленную в JEP 280, которую мы обсуждаем в этом посте.

Следующие два листинга показывают аналогичные реализации простого приложения, показанного выше, но вместо конкатенации строк они используют StringBuilder и StringBuffer соответственно. Когда javap -verbose выполняется для этих классов после того, как они скомпилированы с JDK 8 и с JDK 11, в main(String []) методах нет существенных различий.

Явное использование StringBuilder в JDK 8 и JDK 11 одинаково

Явное использование StringBuffer в JDK 8 и JDK 11 одинаково

JDK 8 и JDK 11 Обработка зацикленных конкатенаций строк

Для последнего примера изменений в JEP 280 в действии я использую пример кода, который может нарушить восприимчивость некоторых разработчиков Java и выполнить конкатенацию строк в цикле. Имейте в виду, что это только иллюстративный пример, и все будет хорошо, но не пытайтесь повторять это дома.

В презентации «Enough java.lang.String to Hang Ourselves . », доктор Хайнц М. Кабуц (Heinz M. Kabutz) и Дмитрий Вязеленко (Dmitry Vyazelenko) обсуждают внесенные изменения в конкатенацию строк Java и кратко их обобщают, “+ больше не компилируется в StringBuilder”. На слайде «Lessons from Today» они заявляют: «Используйте + вместо StringBuilder, где это возможно» и «Перекомпилируйте классы для Java 9+».

Изменения, реализованные в JDK 9 с JEP 280, «позволят в будущем оптимизировать конкатенацию строк, не требуя дополнительных изменений в байт-коде, генерируемом javac». Интересно, что недавно было объявлено, что JEP 348 («Java Compiler Intrinsics for JDK APIs») теперь кандидат в JEP, и его целью является использование аналогичного подхода для компиляции методов String::format и Objects::hash .

Как считаете, полезная статья? Ждем ваши комментарии и приглашаем всех на день открытых дверей по курсу «Разработчик Java», который уже 25 марта проведет генеральный директор компании ОТУС — Виталий Чибриков.

Конкатенация строк в Java — когда использовать +, StringBuilder и concat [duplicate]

Когда мы должны использовать + для конкатенации строк, когда предпочтительнее StringBuilder и когда подходит для использования concat.

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

9 ответов

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

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

Как указывали другие, когда вы используете + несколько раз в рамках одного и того же оператора, компилятор может часто оптимизировать это для вас. Однако, по моему опыту, этот аргумент не применяется, когда конкатенации происходят в отдельных утверждениях. Это, конечно, не помогает с циклами.

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

P.S. Мне никогда не приходилось использовать concat .

Современный компилятор Java преобразует ваши + операции в приложение StringBuilder. Я хочу сказать, если вы выполните str = str1 + str2 + str3 , тогда компилятор сгенерирует следующий код:

Вы можете декомпилировать код с помощью DJ или Cavaj, чтобы подтвердить это:) Итак, теперь это более важный выбор, чем преимущество в производительности для использования + или StringBuilder:)

Однако, учитывая ситуацию, когда компилятор не делает этого для вашего (если вы используете какой-либо частный Java SDK для этого, это может произойти), то, безусловно, StringBuilder — это путь, по которому вы избегаете большого количества ненужных String объектов.

Почему не рекомендуются множественные конкатенации string

Это хорошие способы. Почему?

user avatar

user avatar

user avatar

user avatar

  • Используйте StringBuilder , когда вам нужно построить строку в цикле
    • + отлично подходит для простой конкатенации, но ужасно для инкрементной сборки
    • Конкатенация строк Java
    • Учебники по Java — общие сведения
    • Используйте StringBuilder — это намного эффективнее для этих последовательных конкатенаций
    • Используйте StringBuilder(int capacity) , чтобы указать вероятную необходимую емкость, если есть способ ее предвидеть (используется средний размер выше, но другие методы может быть более удобным)
    • Используйте параметр параметра Collection , чтобы обеспечить более эффективную структуру данных, чем Vector , который синхронизирован — плюс вызывающий имеет гораздо большую гибкость (например, нет необходимости копировать Set<String> в Vector<String> только для вызова этого метода).
    • Простые случаи жесткого кода, если они вероятны (например, null , размер 0 и размер 1 выше).
    • Используйте final , чтобы облегчить встраивание и оптимизацию JIT
    • Загрузите размер strings , если он используется несколько раз. (например, используется 3 раза в приведенном выше коде.)
    • Веревки для статьи Java — http://www.ibm.com/developerworks/java/library/j-ropes
    • Веревки реализации Java — http://ahmadsoft.org/ropes/
    • Веревки (Википедия) — http://en.wikipedia.org/wiki/Rope_(computer_science)

    class MyTimer private final long start;

    public long getElapsed () return System.currentTimeMillis () — start;
    >
    >

    public class AppDemo1 static final int N = 47500 ;

    public static void main ( String args [])

    Разница в «+» и StringBuffer

    class MyPoint private final int x, y;
    private final String cache;

    class MyTimer private final long start;

    public long getElapsed () return System.currentTimeMillis () — start;
    >
    >

    public class AppDemo2 static final int N = 1000000 ;

    public static void main ( String args []) MyPoint mp = new MyPoint ( 37 , 47 ) ;
    String s1 = null ;
    String s2 = null ;
    String s3 = null ;
    String s4 = null ;

Похожие статьи