Semperante
4 уровень

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

Пост из группы Random
937284 участников
В данной статье, хотелось бы рассмотреть, разобрать, рассказать и показать, в чем же разница между методом append из класса StringBuilder и оператором += для String. Здесь вопрос будет стоять не столько в областях применения, сколько в оптимизации кода.
Да, бесспорно не углублявшийся в этот вопрос человек, скажет: "Зачем мне заменять оператор += созданием нового объекта вообще другого класса, после чего вызывать ещё и метод 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 и не знаю что там и как). Это наглядный пример того, что лишний объект совершенно не всегда является затратным. Да, код станет на пару строк длиннее, но зато экономия времени в больших проектах может стать колоссальной. Прошу заметить, что замеры производились далеко не на офисном ПК с доисторическим процессором, а теперь представьте, какая разница будет на этом самом офисном компьютере, который с трудом тянет косынку.
Комментарии (10)
  • популярные
  • новые
  • старые
Для того, что бы оставить комментарий вы должны авторизироваться
Nikita Koliadin 40 уровень, Днепр
22 июня, 13:59
Почитайте документацию Java 9. Вот статейка хорошая, вот ее перевод. До java 8 можно использовать метод join у стринга. Результат такой же. Также есть коллектор, вот А вот инфа о том что, зачем, и что дало изменение в классе String
Viacheslav 3 уровень, Санкт-Петербург
22 июня, 14:18
Про 9 интересно ) Спасибо )
Viacheslav 3 уровень, Санкт-Петербург
22 июня, 12:50
О том, как работает Compound Assignment Operators: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.1 То есть += можем читать как просто s = s + "строка" Читаем про то, что при конкатенации создаётся НОВЫЙ StringBuilder. Каждый раз. Простой способ это увидеть - посмотреть байткод скомпилированного класса. Накладные расходы связаны не с оператором, а с тем, что создаётся много ненужных объектов + работа GC.
Viacheslav 3 уровень, Санкт-Петербург
22 июня, 12:52
Подробнее про байткод можно посмотреть тут: https://javarush.ru/groups/posts/645-stroki-v-java
Игорь 40 уровень, Киев
22 июня, 09:39
String immutable, а StringBuilder mutable. Вот, имхо, и вся разница. Соответственно, т.к. строка не изменяема, то в цикле каждый раз создается новая строка (старая становится доступна для сборщика мусора) . В случае StringBuilder символы просто добавляются к существующему обьекту StringBuilder. Отсюда и разница в скорости. P.S.: Про mutable/immutable подробно рассказывается позже 4го уровня(https://javarush.ru/quests/lectures/questmultithreading.level02.lecture01). P.P.S.: Думаю, администрации стоит задуматься над запретом создавать посты людям ниже определенного левела. Или ввести обязательную премодерацию. Ибо новички начитаются чувствую.
Стас Пасинков 26 уровень, Киев
22 июня, 12:32
что за дискриминация по уровню, лол? я думаю, человек в курсе про mutable/immutable. статья ведь не об этом, а о скорости. и даже если не в курсе - все-равно, статья вполне годная. с примерами кода, с результатами работы. человека заинтересовала эта тема, он провел исследование, получил результаты и поделился ими. что ж тут плохого?) а разница в скорости из-за того, что на строках просто сборщик мусора влючался (100к объектов без ссылок на них - это очень вкусно, по мнению gc). и, скорее всего, не единожды)) чтобы убедиться - можно замерять наносекунды но на циклах не до 100к, а просто от 1 до 10. разница там будет уже совсем не такая уж и большая.
Игорь 40 уровень, Киев
22 июня, 12:48
Никакой дискриминации. Просто считаю что учиться нужно читая грамотную и выверенную информацию. До нововведений в данном разделе так и было(обсуждения всеми и всями велись на форуме). Сейчас уже нет. Для меня нет.
Стас Пасинков 26 уровень, Киев
22 июня, 14:33
я про эту вашу фразу
Думаю, администрации стоит задуматься над запретом создавать посты людям ниже определенного левела.
считаю, что если у вас такие взгляды - то это свидетельствует о раздутом ЧСВ. не надо так) а то и мне еще запретят посты создавать))
Boris Ivanov 19 уровень
29 июня, 09:16
Автор статьи поднял интересную тему, но да, изложить ее стоило именно с точки зрения особенностей mutable/immutable.
Стас Пасинков 26 уровень, Киев
22 июня, 02:12
а байткод не смотрели какой получается при аппенде? может там компилятор сразу пишет че-то типа
StringBuilder sb = new StringBuilder("Привет012345678910111213...");
на таком примере эта оптимизация была бы очевидна :)