Строки - классы StringBuffer и StringBuider

Ранее рассмотренный класс строк String позволяет создавать и обрабатывать строки в виде неизменяемого объекта. То есть, при обработке строк часто создавался новый объект класса 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 (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

Получение и изменение символов

Метод 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 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 strBuffer = new StringBuffer("hello");
strBuffer.setLength(10);
System.out.println(strBuffer.toString()); //"hello     "
 
strBuffer.setLength(4);
System.out.println(strBuffer.toString()); //"hell"

Замена подстроки в строке

Для замены подстроки между определенными позициями на другую подстроку применяется метод 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.