StringBuilder — Java: Массивы
Генерация строк в циклах — задача, часто возникающая на практике. Типичный пример — метод, помогающий генерировать HTML-списки. Он принимает на вход массив элементов и возвращает HTML-список из них:
Как можно решить эту задачу «в лоб»:
- Создать переменную result и записать в нее
-
.
- Пройтись циклом по элементам коллекции и дописать в результирующую строку очередной элемент
- .
- Добавить в конце
и вернуть result из функции.
Такой способ вполне рабочий, но для большинства языков программирования максимально неэффективный. Дело в том, что конкатенация порождает новую строчку вместо старой, — и подобная ситуация повторяется на каждой итерации. Причем строка становится все больше и больше. Копирование строк приводит к серьезному расходу памяти и влияет на производительность.
Правильный способ для динамической сборки строк в Java — использование класса StringBuilder . Он позволяет собирать строку по кусочкам без лишнего копирования. Перепишем наш пример:
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Что такое stringbuilder java
Объекты String являются неизменяемыми, поэтому все операции, которые изменяют строки, фактически приводят к созданию новой строки, что сказывается на производительности приложения. Для решения этой проблемы, чтобы работа со строками проходила с меньшими издержками в Java были добавлены классы StringBuffer и StringBuilder . По сути они напоминает расширяемую строку, которую можно изменять без ущерба для производительности.
Эти классы похожи, практически двойники, они имеют одинаковые конструкторы, одни и те же методы, которые одинаково используются. Единственное их различие состоит в том, что класс StringBuffer синхронизированный и потокобезопасный. То есть класс StringBuffer удобнее использовать в многопоточных приложениях, где объект данного класса может меняться в различных потоках. Если же речь о многопоточных приложениях не идет, то лучше использовать класс StringBuilder, который не потокобезопасный , но при этом работает быстрее, чем StringBuffer в однопоточных приложениях.
StringBuffer определяет четыре конструктора:
StringBuffer() StringBuffer(int capacity) StringBuffer(String str) StringBuffer(CharSequence chars)
Аналогичные конструкторы определяет StringBuilder:
StringBuilder() StringBuilder(int capacity) StringBuilder(String str) StringBuilder(CharSequence chars)
Рассмотрим работу этих классов на примере функциональности StringBuffer.
При всех операциях со строками StringBuffer / StringBuilder перераспределяет выделенную память. И чтобы избежать слишком частого перераспределения памяти, StringBuffer/StringBuilder заранее резервирует некоторую область памяти, которая может использоваться. Конструктор без параметров резервирует в памяти место для 16 символов. Если мы хотим, чтобы количество символов было иным, то мы можем применить второй конструктор, который в качестве параметра принимает количество символов.
Третий и четвертый конструкторы обоих классов принимают строку и набор символов, при этом резервируя память для дополнительных 16 символов.
С помощью метода capacity() мы можем получить количество символов, для которых зарезервирована память. А с помощью метода ensureCapacity() изменить минимальную емкость буфера символов:
String str = "Java"; StringBuffer strBuffer = new StringBuffer(str); System.out.println("Емкость: " + strBuffer.capacity()); // 20 strBuffer.ensureCapacity(32); System.out.println("Емкость: " + strBuffer.capacity()); // 42 System.out.println("Длина: " + strBuffer.length()); // 4
Так как в самом начале StringBuffer инициализируется строкой «Java», то его емкость составляет 4 + 16 = 20 символов. Затем мы увеличиваем емкость буфера с помощью вызова strBuffer.ensureCapacity(32) повышаем минимальную емкость буфера до 32 символов. Однако финальная емкость может отличаться в большую сторону. Так, в данном случае я получаю емкость не 32 и не 32 + 4 = 36, а 42 символа. Дело в том, что в целях повышения эффективности Java может дополнительно выделять память.
Но в любом случае вне зависимости от емкости длина строки, которую можно получить с помощью метода length() , в StringBuffer остается прежней — 4 символа (так как в «Java» 4 символа).
Чтобы получить строку, которая хранится в StringBuffer, мы можем использовать стандартный метод toString() :
String str = "Java"; StringBuffer strBuffer = new StringBuffer(str); System.out.println(strBuffer.toString()); // Java
По всем своим операциям StringBuffer и StringBuilder напоминают класс String.
Получение и установка символов
Метод charAt() получает, а метод setCharAt() устанавливает символ по определенному индексу:
StringBuffer strBuffer = new StringBuffer("Java"); char c = strBuffer.charAt(0); // J System.out.println(c); strBuffer.setCharAt(0, 'c'); System.out.println(strBuffer.toString()); // cava
Метод getChars() получает набор символов между определенными индексами:
StringBuffer strBuffer = new StringBuffer("world"); int startIndex = 1; int endIndex = 4; char[] buffer = new char[endIndex-startIndex]; strBuffer.getChars(startIndex, endIndex, buffer, 0); System.out.println(buffer); // orl
Добавление в строку
Метод append() добавляет подстроку в конец StringBuffer:
StringBuffer strBuffer = new StringBuffer("hello"); strBuffer.append(" world"); System.out.println(strBuffer.toString()); // hello world
Метод insert() добавляет строку или символ по определенному индексу в StringBuffer:
StringBuffer strBuffer = new StringBuffer("word"); strBuffer.insert(3, 'l'); System.out.println(strBuffer.toString()); //world strBuffer.insert(0, "s"); System.out.println(strBuffer.toString()); //sworld
Удаление символов
Метод delete() удаляет все символы с определенного индекса о определенной позиции, а метод deleteCharAt() удаляет один символ по определенному индексу:
StringBuffer strBuffer = new StringBuffer("assembler"); strBuffer.delete(0,2); System.out.println(strBuffer.toString()); //sembler strBuffer.deleteCharAt(6); System.out.println(strBuffer.toString()); //semble
Обрезка строки
Метод substring() обрезает строку с определенного индекса до конца, либо до определенного индекса:
StringBuffer strBuffer = new StringBuffer("hello java!"); String str1 = strBuffer.substring(6); // обрезка строки с 6 символа до конца System.out.println(str1); //java! String str2 = strBuffer.substring(3, 9); // обрезка строки с 3 по 9 символ System.out.println(str2); //lo jav
Изменение длины
Для изменения длины StringBuffer (не емкости буфера символов) применяется метод setLength() . Если StringBuffer увеличивается, то его строка просто дополняется в конце пустыми символами, если уменьшается — то строка по сути обрезается:
StringBuffer strBuffer = new StringBuffer("hello"); strBuffer.setLength(10); System.out.println(strBuffer.toString()); //"hello " strBuffer.setLength(4); System.out.println(strBuffer.toString()); //"hell"
Замена в строке
Для замены подстроки между определенными позициями в StringBuffer на другую подстроку применяется метод replace() :
StringBuffer strBuffer = new StringBuffer("hello world!"); strBuffer.replace(6,11,"java"); System.out.println(strBuffer.toString()); //hello java!
Первый параметр метода replace указывает, с какой позиции надо начать замену, второй параметр — до какой позиции, а третий параметр указывает на подстроку замены.
Обратный порядок в строке
Метод reverse() меняет порядок в StringBuffer на обратный:
StringBuffer strBuffer = new StringBuffer("assembler"); strBuffer.reverse(); System.out.println(strBuffer.toString()); //relbmessa
Классы StringBuffer и StringBuilder
Класс String представляет собой неизменяемые последовательности символов постоянной длины и частое использование объектов класса занимают много места в памяти. Класс StringBuffer представляет расширяемые и доступные для изменений последовательности символов, позволяя вставлять символы и подстроки в существующую строку и в любом месте. Данный класс гораздо экономичнее в плане потребления памяти и настоятельно рекомендуется к использованию.
Существует четыре конструктора класса:
- StringBuffer() — резервирует место под 16 символов без перераспределения памяти
- StringBuffer(int capacity) — явно устанавливает размер буфера
- StringBuffer(String string) — устанавливает начальное содержимое и резервирует 16 символов без повторого резервирования
- StringBuffer(CharSequence cs) — создаёт объект, содержащий последовательность символов и резервирует место ещё под 16 символов
Методы класса StringBuffer
length()
Метод позволяет получить текущую длину объекта.
StringBuffer sb = new StringBuffer("Котэ"); mInfoTextView.setText("Длина: " + sb.length());
capacity()
Метод позволяет получить текущий объём выделенной памяти.
StringBuffer sb = new StringBuffer("Котэ"); mInfoTextView.setText("Объём памяти: " + sb.capacity()); // вернёт 20
Обратите внимание, что хотя слово состоит из четырёх символов, в памяти выделено запасное пространство для дополнительных 16 символов. Для такой простейшей операции выигрыша нет, но в сложных примерах, когда приходится на лету соединять множество строк, производительность резко возрастает.
ensureCapacity()
Можно предварительно выделить место для определённого количества символов, если собираетесь добавлять большое количество маленьких строк.
setLength(int length)
Устанавливает длину строки. Значение должно быть неотрицательным.
charAt(int index) и setCharAt(int index, char ch)
Можно извлечь значение отдельного символа с помощью метода charAt() и установить новое значение символа с помощью метода setCharAt(), указав индекс символа и его значение.
StringBuffer sb = new StringBuffer("Кит"); sb.setCharAt(1, 'o'); mInfoTextView.setText(sb.toString());
getChars()
Позволяет скопировать подстроку из объекта класса StringBuffer в массив. Необходимо позаботиться, чтобы массив был достаточного размера для приёма нужного количества символов указанной подстроки.
append()
Метод соединяет представление любого другого типа данных. Есть несколько перегруженных версий.
StringBuffer append(String string) StringBuffer append(int number) StringBuffer append(Object object)
Строковое представление каждого параметра за кулисами получают через метод String.valueOf() и затем полученные строки склеиваются в итоговую строку.
String str1 = "У кота "; String str2 = " лапы"; int paws = 4; StringBuffer sb = new StringBuffer(20); sb.append(str1).append(paws).append(str2); mInfoTextView.setText(sb.toString());
insert()
Вставляет одну строку в другую. Также можно вставлять значения других типов, которые будут автоматически преобразованы в строки. Вам надо указать индекс позиции, куда будет вставляться строка.
StringBuffer sb = new StringBuffer("Я Котов"); sb.insert(2, "Люблю "); mInfoTextView.setText(sb.toString());
reverse()
Позволяет изменить порядок символов на обратный.
StringBuffer sb = new StringBuffer("МОКНЕТ ОКСАНА С КОТЕНКОМ"); sb.reverse(); mInfoTextView.setText(sb.toString());
У меня получилось практически то же самое, может метод глючит?
delete() и deleteCharAt()
Метод delete() удаляет последовательность символов, вам надо задать индекс первого символа, который надо удалить, а также индекс символа, следующего за последним из удаляемых. Метод deleteCharAt() удаляет один символ из указанной позиции.
replace()
Позволяет заменить один набор символов на другой. Нужно указать начальный и конечный индекс и строку замены.
substring()
Позволяет получить часть содержимого. Есть две формы метода. В первом случае нужно указать индекс начала позиции, с которой нужно получить подстроку. Во втором варианте указывается начальный индекс и конечный индекс, если нужно получить текст из середины строки.
Есть и другие методы
StringBuilder
Класс StringBuilder идентичен классу StringBuffer и обладает большей производительностью. Однако он не синхронизирован, поэтому его не нужно использовать в тех случаях, когда к изменяемой строке обращаются несколько потоков.
Создадим новый объект.
StringBuilder builder = new StringBuilder();
Добавляем новый фрагмент в существующую строку:
builder.append(ch); // можно добавить один символ builder.append(sometext); // а можно добавить готовую строку String completedString = builder.toString(); // результирующая строка
Соединять строки можно цепочкой.
new StringBuilder().append(s1).append(s2).append(s3).toString();
Данный код работает чуть быстрее, чем вызов append() по отдельности. Но это будет заметно при очень больших объёмах.
StringBuilder, StringBuffer
— Осталось разобраться с StringBuilder и считай, что мы закончили.
Как ты уже знаешь, StringBuilder – это аналог класса String, только изменяемый.
— А еще я помню, что компилятор сам генерирует код с использованием StringBuilder, когда мы просто складываем строки.
— Да, ты прав. Какая у тебя отличная память. Впрочем, как и у всех роботов. Все время забываю об этом.
Давай же разберемся, что можно делать с помощью класса StringBuilder:
1) У меня есть обычная строка, я хочу сделать ее изменяемой. Как мне это сделать?
String s = "Bender"; StringBuilder s2 = new StringBuilder(s);
2) Я хочу добавить символ к текущей «изменяемой строке»?
String s = "Bender"; StringBuilder s2 = new StringBuilder(s); s2.append("!");
3) А как преобразовать StringBuilder обратно в строку?
String s = "Bender"; StringBuilder s2 = new StringBuilder(s); s2.append("!"); s = s2.toString();
4) А если мне нужно удалить символ?
String s = "Bender"; StringBuilder s2 = new StringBuilder(s); s2.deleteCharAt(2); //останется "Beder"
5) Я хочу заменить часть строки на другую?
String s = "Bender"; StringBuilder s2 = new StringBuilder(s); s2.replace (3, 5, "_DE_"); //будет "Ben_DE_r"
6) Мне нужно развернуть строку задом наперед?
String s = "Bender"; StringBuilder s2 = new StringBuilder(s); s2.reverse(); //будет "redneB";
— Круто. Спасибо, Элли, все понятно.
— Рада, что тебе понравилось.
Еще хотела бы напомнить, вроде об этом должен был сказать Билаабо.
Есть еще один класс – StringBuffer – это аналог класса StringBuilder, только его методы объявлены как synchronized. А значит, перед каждым вызовом такого метода Java-машина проверяет, занят ли объект, и если нет — помечает его как занятый. После выхода из метода – «освобождает» объект. Из-за этого такие вызовы работают медленнее. Не стоит без необходимости пользоваться StringBuffer.
Но если тебе нужен изменяемый объект String, к которому будут обращаться из нескольких нитей – лучше StringBuffer тебе не найти.
— Спасибо за информацию. Думаю, когда-нибудь она мне пригодится.