В чем разница между String, StringBuilder и StringBuffer в Java
Что проверяют
Понимаете ли вы, что стоит за неизменяемостью строк и почему конкатенация в цикле дорогая. Следом обычно спрашивают, что компилятор делает с оператором + и правда ли StringBuffer делает код потокобезопасным.
Как отвечать
String: неизменяемый иfinal, любая «модификация» возвращает новый объект. Это даёт безопасный ключ дляHashMap(хэш кешируется), передачу между потоками без синхронизации и пул строк: литералы интернируются,intern()делает это вручную;StringBuilder: изменяемый буфер сappend,insert,reverse, без синхронизации и потому самый быстрый. Начальная ёмкость 16 символов, при переполнении массив пересоздаётся примерно вдвое больше, так что при известном размере ёмкость лучше задать в конструкторе;StringBuffer: тот же API, но методыsynchronized. Он есть с Java 1.0, аStringBuilderдобавили в Java 5 как замену для однопоточного кода.
Одиночное выражение a + b + c компилятор оптимизирует сам: до Java 9 javac собирал цепочку StringBuilder, с Java 9 (JEP 280) это вызов через invokedynamic и StringConcatFactory. Но в цикле каждая итерация всё равно создаёт новую строку, отсюда квадратичная сложность и нагрузка на GC. Там нужен явный StringBuilder, а для склейки коллекции подойдут String.join или Collectors.joining.
Плюсом будет упомянуть compact strings из Java 9: внутри String лежит byte[] с флагом кодировки, и латиница занимает байт на символ.
Где спотыкаются
Говорят, что StringBuffer нужен для многопоточности. Синхронизирован каждый метод по отдельности, последовательность вызовов атомарной от этого не становится. Общий изменяемый буфер между потоками почти не встречается, и в новом коде StringBuffer не пишут.
Сравнивают строки через ==. Это сравнение ссылок, оно «работает» на литералах из пула и ломается на строках из ввода или конкатенации.
Утверждают, что + всегда медленный. Однострочную склейку компилятор делает эффективно сам, ручной StringBuilder там только ухудшает читаемость.