Строки интенсивно используются в программах на Java. Строки представляют собой последовательность символов. Для создания строки, обычно используется строковый литерал. Одна из особенностей строк в Java – они иммутабельны, то есть после создания они не могут быть изменены. Все операции над строками порождают новые строки, но никогда не меняют уже созданные. Это приводит к тому, что при интенсивных операциях над строками создаётся и высвобождается излишнее количество объектов с очень коротким временем жизни. Для решения этой проблемы были созданы StringBuffer
и несколько позднее – StringBuilder
.
Конкатенация с помощью оператора +
В самом простом случае можно объединить строки, воспользовавшись оператором сложения:
String hello = "Hello"; String world = " World"; System.out.println(hello + world);
Результатом выполнения данного кода будет вывод строки "Hello World"
в консоль. Это самый простой и в большинстве случаев подходящий способ конкатенации строк в Java.
StringBuilder
и StringBuffer
, в отличие от класса String
, созданы для того, чтобы иметь возможность изменять своё строковое значение. Оба эти класса инкапсулируют в себе массив байтов, в который добавляются новые значения. Когда длины массива недостаточно, выделяется память под массив большей размерности и все данные записываются в него.
Для чего используется StringBuilder
Обычно StringBuilder используется, если нужно создать строку в несколько этапов, используя несколько операций конкатенации, или же в цикле:
StringBuilder s = new StringBuilder(); s.append("Hello"); s.append(" World"); s.append(" in Java!"); System.out.println(s.toString());
С помощью метода StringBuilder.append()
мы изменяем значение, хранящееся внутри экземпляра StringBuilder
. Эта операция очень дешёвая в отличие создания новых объектов.
Давайте сравним производительность создания в цикле множества объектов String
и использование StringBuilder
. У нас есть два цикла:
long start = System.currentTimeMillis(); String s1 = ""; for (int i=0; i< 50000; i++) { s1 = s1 + "a"; } System.out.println(System.currentTimeMillis() - start); long start2 = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for (int i=0; i< 50000; i++) { sb.append("a"); } System.out.println(System.currentTimeMillis() - start2);
В первом цикле в одну и ту же переменную типа String
записывается вновь создаваемый объект строки, получаемый с помощью конкатенации двух строк (s1 + "a"
). Это приводит к тому, что JVM постоянно создаёт новые объекты, захламляя память и тратя дополнительные ресурсы на освобождение памяти от мусора. Операции по выделению памяти под новый массив гораздо дешевле, поэтому в данном случае StringBuilder
выигрывает в скорости по сравнению с String
.
Для чего нужен StringBuffer
StringBuffer
– это предшественник StringBuilder
с теми же методами, но имеющий характерную особенность – методы StringBuffer
синхронизированы, то есть класс потокобезопасен. StringBuilder
же не является потокобезопасным, следовательно, его экземпляры должны использоваться только в одном потоке.
StringBuffer
появился ещё в Java 1.0, а StringBuilder
– в Java 5. В большинстве случаев, выбирая между этими двумя классами, рекомендуется использовать именно StringBuilder
, если не требуется потокобезопасность.
Выводы
StringBuilder
используется как эффективная промежуточная замена String
в тех случаях, когда идёт интенсивная работа по объединению (замене, вставке, удалению) строк, а также при конкатенации строк в цикле. StringBuffer
используется в тех же случаях, но является потокобезопасным (и несколько более медленным из-за синхронизированных методов).