JavaRush /Блог /Random /String += String или StringBuilder.append(String)?
SemperAnte
4 уровень
Донецк

String += String или StringBuilder.append(String)?

Статья из группы Random
В данной статье, хотелось бы рассмотреть, разобрать, рассказать и показать, в чем же разница между методом append из класса StringBuilder и оператором += для String. Здесь вопрос будет стоять не столько в областях применения, сколько в оптимизации кода.
String += String или StringBuilder.append(String)? - 1
Да, бесспорно не углублявшийся в этот вопрос человек, скажет: "Зачем мне заменять оператор += созданием нового объекта вообще другого класса, после чего вызывать ещё и метод toString()? О какой оптимальности речь? Выбор ведь очевиден, ты о чем вообще?" и будет совершенно не прав. Одна из проблем состоит в том, что String не является примитивом. String - объект как и любой class в Java, а как известно, в Java нет такого понятия как перегрузка операторов (Как например в С++), операторы определены только для примитивов, для любого же класса мы с вами не может переопределить никакой оператор. Именно поэтому операторы "+" и "+=" являются своего рода "костылями" библиотеки Java, а костыль всегда несет потери. Собственно, давайте перестанем тянуть хвост за кота и перейдем к замерам. Вот простая программка замеряющая время "склеивания" строки с переменной цикла 100000 раз.

public class MainClass
{
    private static long time;

    public static void main(String[] args)
    {

        saveTime();
        String s = "Привет";
        for(int i = 0; i < 100000; ++i)
        {
            s+=i;
        }
        printDiff();

    }
    private static void saveTime()
    {
        time = System.currentTimeMillis();
    }

    private static void printDiff()
    {
        System.out.println((System.currentTimeMillis() - time) + "ms");
    }
}
На моем компьютере в консоль вывелось 6815ms. Т.е моему компьютеру понадобилось почти 7 секунд, чтобы склеить эту строку. Теперь же давайте заменим на StringBuilder и даже включим метод toString() в замеры.

public class MainClass
{
    private static long time;

    public static void main(String[] args)
    {

        saveTime();
        StringBuilder sb = new StringBuilder("Привет");
        for(int i = 0; i < 100000; ++i)
        {
            sb.append(i);
        }
        String s = sb.toString();
        printDiff();

    }
    private static void saveTime()
    {
        time = System.currentTimeMillis();
    }

    private static void printDiff()
    {
        System.out.println((System.currentTimeMillis() - time) + "ms");
    }
}
Мой ПК сказал мне 10ms. Т.е 0.01 Секунды. Думаю, разница на практике весьма очевидна, грубо говоря в 700 раз append отработал быстрее. Вызвано это тем, что скорее всего "+" и "+=" и сами могут вызывать этот же append, но при этом пройдя долгий путь по костылям языка, чтобы понять, что такой оператор вообще есть и что он должен делать (последний абзац не более чем догадка, я не разработчик JVM и не знаю что там и как). Это наглядный пример того, что лишний объект совершенно не всегда является затратным. Да, код станет на пару строк длиннее, но зато экономия времени в больших проектах может стать колоссальной. Прошу заметить, что замеры производились далеко не на офисном ПК с доисторическим процессором, а теперь представьте, какая разница будет на этом самом офисном компьютере, который с трудом тянет косынку.
Комментарии (12)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
FutureDev Уровень 42
11 мая 2023
Павел Бобров Уровень 16
8 августа 2018
Как-то тут совсем уж грустно с мат.частью. Вся информация, которую можно вынести из статьи - "StringBuilder быстрее чем конкатенация" Тем более учитывая то, что товарищ Шипилёв эту тему разложил по полочкам еще в 15-ом году. В своем докладе — "Катехизис java.lang.String".
Nikita Koliadin Уровень 40
22 июня 2018
Почитайте документацию Java 9. Вот статейка хорошая, вот ее перевод. До java 8 можно использовать метод join у стринга. Результат такой же. Также есть коллектор, вот А вот инфа о том что, зачем, и что дало изменение в классе String
Viacheslav Уровень 3
22 июня 2018
О том, как работает Compound Assignment Operators: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.1 То есть += можем читать как просто s = s + "строка" Читаем про то, что при конкатенации создаётся НОВЫЙ StringBuilder. Каждый раз. Простой способ это увидеть - посмотреть байткод скомпилированного класса. Накладные расходы связаны не с оператором, а с тем, что создаётся много ненужных объектов + работа GC.
Игорь Уровень 40
22 июня 2018
String immutable, а StringBuilder mutable. Вот, имхо, и вся разница. Соответственно, т.к. строка не изменяема, то в цикле каждый раз создается новая строка (старая становится доступна для сборщика мусора) . В случае StringBuilder символы просто добавляются к существующему обьекту StringBuilder. Отсюда и разница в скорости. P.S.: Про mutable/immutable подробно рассказывается позже 4го уровня(https://javarush.com/quests/lectures/questmultithreading.level02.lecture01). P.P.S.: Думаю, администрации стоит задуматься над запретом создавать посты людям ниже определенного левела. Или ввести обязательную премодерацию. Ибо новички начитаются чувствую.
Стас Пасинков Уровень 26 Master
22 июня 2018
а байткод не смотрели какой получается при аппенде? может там компилятор сразу пишет че-то типа

StringBuilder sb = new StringBuilder("Привет012345678910111213...");
на таком примере эта оптимизация была бы очевидна :)