Class StringBuffer
A thread-safe, mutable sequence of characters. A string buffer is like a String , but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls.
String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.
The principal operations on a StringBuffer are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string buffer. The append method always adds these characters at the end of the buffer; the insert method adds the characters at a specified point.
For example, if z refers to a string buffer object whose current contents are «start» , then the method call z.append(«le») would cause the string buffer to contain «startle» , whereas z.insert(4, «le») would alter the string buffer to contain «starlet» .
In general, if sb refers to an instance of a StringBuffer , then sb.append(x) has the same effect as sb.insert(sb.length(), x) .
Whenever an operation occurs involving a source sequence (such as appending or inserting from a source sequence), this class synchronizes only on the string buffer performing the operation, not on the source. Note that while StringBuffer is designed to be safe to use concurrently from multiple threads, if the constructor or the append or insert operation is passed a source sequence that is shared across threads, the calling code must ensure that the operation has a consistent and unchanging view of the source sequence for the duration of the operation. This could be satisfied by the caller holding a lock during the operation’s call, by using an immutable source sequence, or by not sharing the source sequence across threads.
Every string buffer has a capacity. As long as the length of the character sequence contained in the string buffer does not exceed the capacity, it is not necessary to allocate a new internal buffer array. If the internal buffer overflows, it is automatically made larger.
Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown.
As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder . The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.
Class StringBuffer
A thread-safe, mutable sequence of characters. A string buffer is like a String , but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls.
String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.
The principal operations on a StringBuffer are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string buffer. The append method always adds these characters at the end of the buffer; the insert method adds the characters at a specified point.
For example, if z refers to a string buffer object whose current contents are «start» , then the method call z.append(«le») would cause the string buffer to contain «startle» , whereas z.insert(4, «le») would alter the string buffer to contain «starlet» .
In general, if sb refers to an instance of a StringBuffer , then sb.append(x) has the same effect as sb.insert(sb.length(), x) .
Whenever an operation occurs involving a source sequence (such as appending or inserting from a source sequence), this class synchronizes only on the string buffer performing the operation, not on the source. Note that while StringBuffer is designed to be safe to use concurrently from multiple threads, if the constructor or the append or insert operation is passed a source sequence that is shared across threads, the calling code must ensure that the operation has a consistent and unchanging view of the source sequence for the duration of the operation. This could be satisfied by the caller holding a lock during the operation’s call, by using an immutable source sequence, or by not sharing the source sequence across threads.
Every string buffer has a capacity. As long as the length of the character sequence contained in the string buffer does not exceed the capacity, it is not necessary to allocate a new internal buffer array. If the internal buffer overflows, it is automatically made larger.
Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown.
As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder . The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.
Знакомство со String, StringBuffer и StringBuilder в Java
Для работы с текстовыми данными в Java есть три класса: String, StringBuffer и StringBuilder. С первым каждый разработчик сталкивается еще в самом начале изучения языка. А что насчет оставшихся двух? Какие у них есть различия, и когда лучше использовать тот или иной класс? В общем-то, разница между ними небольшая, но лучше во всем разобраться на практике 🙂
- Класс String в Java
- Класс StringBuffer в Java
- Класс StringBuilder в Java
Класс String
- это immutable (неизменный) класс
- это final класс
- Благодаря неизменности, хэшкод экземпляра класса String кэшируется. Его не нужно вычислять каждый раз, потому что значения полей объекта никогда не изменятся после его создания. Это дает высокую производительность при использовании данного класса в качестве ключа для HashMap .
- Класс String можно использовать в многопоточной среде без дополнительной синхронизации.
- Еще одна особенность класса String — для него перегружен оператор » + » в Java. Поэтому конкатенация (сложение) строк выполняется довольно просто:
public static void main(String[] args) < String command = "Follow" + " " + "the" + " " + "white" + " " + "rabbit"; System.out.println(command); // Follow the white rabbit >
Под капотом конкатенация строк выполняется классом StringBuilder либо StringBuffer (на усмотрение компилятора) и методом append (об этих классах поговорим чуть позже). Если мы будем складывать экземпляры класса String с экземплярами других классов, последние будут приводиться к строковому представлению:
public static void main(String[] args) < Boolean b = Boolean.TRUE; String result = "b is " + b; System.out.println(result); //b is true >
Это еще одно интересное свойство класса String: объекты любых классов можно привести к строковому представлению, используя метод toString() , определенный в классе Object и наследуемый всеми остальными классами. Часто метод toString() у объекта вызывается неявно. Например когда мы выводим что-то на экран или складываем String с объектом другого класса. У класса String есть еще одна особенность. Все строковые литералы, определенные в Java коде, вроде «asdf», на этапе компиляции кэшируются и добавляются в так называемый пул строк. Если мы запустим следующий код:
String a = "Wake up, Neo"; String b = "Wake up, Neo"; System.out.println(a == b);
Мы увидим в консоли true, потому что переменные a и b в действительности будут ссылаться на один и тот же экземпляр класса String, добавленный в пул строк на этапе компиляции. То есть, разные экземпляры класса с одинаковым значением не создаются, и память экономится.
Недостатки:
- перевод строк в разные регистры;
- извлечение подстрок;
- конкатенация;
- и т.д.
public static void main(String[] args)
С первого взгляда кажется, что мы всего-то перевели строку «Wake up, Neo!» в верхний регистр, удалили из данной строки лишние пробелы и обернули в кавычки. На самом деле, в силу неизменности класса String, в результате каждой операции создаются новые экземпляры строк, а старые отбрасываются, порождая большое количество мусора. Как же избежать нерационального использования памяти?
Класс StringBuffer
- StringBuffer() — создаст пустой (не содержащий символов) объект
- StringBuffer(String str) — создаст объект на основе переменной str (содержащий все символы str в той же последовательности)
StringBuffer sb = new StringBuffer(); StringBuffer sb2 = new StringBuffer("Not empty");
Конкатенация строк через StringBuffer в Java выполняется с помощью метода append . Вообще метод append в классе StringBuffer перегружен таким образом, что может принимать в себя практически любой тип данных:
public static void main(String[] args) < StringBuffer sb = new StringBuffer(); sb.append(new Integer(2)); sb.append("; "); sb.append(false); sb.append("; "); sb.append(Arrays.asList(1,2,3)); sb.append("; "); System.out.println(sb); // 2; false; [1, 2, 3]; >
Метод append возвращает объект, на котором был вызван (как и многие другие методы), что позволяет вызывать его “цепочкой”. Пример выше можно написать так:
public static void main(String[] args) < StringBuffer sb = new StringBuffer(); sb.append(new Integer(2)) .append("; ") .append(false) .append("; ") .append(Arrays.asList(1,2,3)) .append("; "); System.out.println(sb); // 2; false; [1, 2, 3]; >
- delete(int start, int end) — удаляет подстроку символов начиная с позиции start , заканчивая end
- deleteCharAt(int index) — удаляет символ в позиции index
- insert(int offset, String str) — вставляет строку str в позицию offset . Метод insert также перегружен и может принимать различные аргументы
- replace(int start, int end, String str) — заменит все символы начиная с позиции start до позиции end на str
- reverse() — меняет порядок всех символов на противоположный substring(int start) — вернет подстроку, начиная с позиции start
- substring(int start, int end) — вернет подстроку, начиная с позиции start до позиции end
public static void main(String[] args) < String numbers = "0123456789"; StringBuffer sb = new StringBuffer(numbers); System.out.println(sb.substring(3)); // 3456789 System.out.println(sb.substring(4, 8)); // 4567 System.out.println(sb.replace(3, 5, "ABCDE")); // 012ABCDE56789 sb = new StringBuffer(numbers); System.out.println(sb.reverse()); // 9876543210 sb.reverse(); // Вернем изначальный порядок sb = new StringBuffer(numbers); System.out.println(sb.delete(5, 9)); // 012349 System.out.println(sb.deleteCharAt(1)); // 02349 System.out.println(sb.insert(1, "One")); // 0One2349 >
Преимущества:
- Как уже сказано, StringBuffer — изменяемый класс, поэтому при работе с ним не возникает такого же количества мусора в памяти, как со String. Поэтому если над строками проводится много модификаций, лучше использовать StringBuffer .
- StringBuffer — потокобезопасный класс. Его методы синхронизированы, а экземпляры могут быть использованы несколькими потоками одновременно.
Недостатки:
С одной стороны, потокобезопасность — преимущество класса, а другой — недостаток. Синхронизированные методы работают медленнее не сихнронизированных. И здесь в игру вступает StringBuilder. Давайте разберемся, что это за класс Java — StringBuilder, какие методы есть и в чем его особенности.
Класс StringBuilder
StringBuilder в Java — класс, представляющий последовательность символов. Он очень похож на StringBuffer во всем, кроме потокобезопасности. StringBuilder предоставляет API, аналогичный API StringBuffer’a. Продемонстрируем это на уже знакомом примере, заменив объявление переменных со StringBufer’а на StringBuilder:
public static void main(String[] args) < String numbers = "0123456789"; StringBuilder sb = new StringBuilder(numbers); System.out.println(sb.substring(3)); //3456789 System.out.println(sb.substring(4, 8)); //4567 System.out.println(sb.replace(3, 5, "ABCDE")); //012ABCDE56789 sb = new StringBuilder(numbers); System.out.println(sb.reverse()); //9876543210 sb.reverse(); // Вернем изначальный порядок sb = new StringBuilder(numbers); System.out.println(sb.delete(5, 9)); //012349 System.out.println(sb.deleteCharAt(1)); //02349 System.out.println(sb.insert(1, "One")); //0One2349 >
Разница лишь в том, что StringBuffer потокобезопасен, и все его методы синхронизированы, а StringBuilder — нет. Это единственная особенность. StringBuilder в Java работает быстрее StringBuffer’а благодаря несинхронизированности методов. Поэтому в большинстве случаев, кроме многопоточной среды, лучше использовать для программы на Java StringBuilder. Резюмируем все в сравнительной таблице трех классов:
String vs StringBuffer vs StringBuilder
String | StringBuffer | StringBuilder | |
---|---|---|---|
Изменяемость | Immutable (нет) | mutable (да) | mutable (да) |
Расширяемость | final (нет) | final (нет) | final (нет) |
Потокобезопасность | Да, за счет неизменяемости | Да, за счет синхронизации | Нет |
Когда использовать | При работе со строками, которые редко будут модифицироваться | При работе со строками, которые часто будут модифицироваться в многопоточной среде | При работе со строками, которые часто будут модифицироваться, в однопоточной среде |