Порівнюємо String, StringBuilder та StringBuffer
Дізнайтеся про ключові відмінності між StringBuilder і StringBuffer Java. Дізнайтеся про їх продуктивність та потокобезпеку, щоб вибрати найкращий варіант.
Для роботи з рядками Java є три основні класи: String , StringBuilder і StringBuffer . Давайте розберемося, який клас колись використовувати.
StringBuffer - потокобезпечний, а StringBuilder немає.
String
Клас String є символьним рядком. Цей клас оголошений, як final , отже цього класу може бути спадкоємців.
Реалізація класу зберігає рядок як незмінного масиву байт. Таким чином, об'єкт класу String не можна змінити, можна тільки створити новий.
У цього є плюси:
- Завдяки незмінності, хешкод екземпляра класу String кешується. Його не потрібно обчислювати щоразу, тому що значення полів об'єкта ніколи не зміняться після його створення. Це дає високу продуктивність при використанні даного класу як ключ для HashMap.
- Клас String можна використовувати у багатопотоковому середовищі без додаткової синхронізації.
- Ще одна особливість класу String - для нього перевантажений оператор "+" у Java. Тому конкатенація рядків виконується досить легко.
Рядкові літерали
Клас String має ще одну особливість. Усі строкові літерали, визначені в Java коді, на кшталт "asdf", на етапі компіляції кешуються та додаються до так званого пулу рядків.
Проте не обійшлось і без проблем. З рядками відбувається безліч дій: конкатенація, переведення у верхній/нижній регістр, витягнуті підрядки, видалення символів у рядку тощо.
І оскільки рядки не змінюються, то для кожної такої операції створюється новий об'єкт у купі. Створення об'єкта в купі вимагає часу, а це призводить до того, що Garbage Collector потрібно буде знаходити і видаляти рядки. Все це позначається на продуктивності програми під час роботи з рядками.
Як уникнути нераціонального використання пам'яті?
StringBuilder
Класи StringBuilder та StringBuffer багато в чому схожі. Обидва класи є мутабельними, тобто вміст рядка може бути змінено після створення.
Вони навіть успадковуються від одного класу AbstractStringBuilder.
Вони також мають схожі конструктори та методи, що дозволяють виконувати конкатенацію рядків, вставку, видалення та багато іншого.
StringBuilder sb = новий StringBuilder(); sb.append("Hello"); sb.append(" "); sb.append("world!"); String finalString = sb.toString();
Навіщо ж тоді два різні класи?
StringBuilder швидше за StringBuffer в однопотокових додатках, так як не має вбудованих механізмів синхронізації . Однак це також означає, що він не є потокобезпечним і не повинен використовуватися в багатопотокових середовищах, якщо не вжито додаткових заходів щодо синхронізації.
З точки зору продуктивності, StringBuilder зазвичай швидше за StringBuffer , оскільки не має накладних витрат на синхронізацію. Однак у більшості програм різниця у швидкості зазвичай незначна, і вибір між цими двома класами має бути заснований на тому, чи є безпека потоків проблемою.
StringBuffer
StringBuffer практично двійник класу StringBuilder : одні й самі методи, які однаково використовуються. Єдина відмінність полягає в тому, що клас StringBuffer синхронізований і потокобезпечний.
Це означає, що якщо кілька потоків одночасно звертаються до одного і того ж об'єкту StringBuffer, вони не заважатимуть один одному. З іншого боку, якщо кілька потоків одночасно звертаються до того самого об'єкта StringBuilder , існує ризик пошкодження даних та інших проблем.
Це робить StringBuffer повільніше StringBuilder в однопотокових додатках, але це необхідно в багатопотокових середовищах для запобігання умовам гонки та інших проблем паралелізму.
Таким чином, StringBuffer варто використовувати в додатках з багатопотоковою обробкою, де об'єкт даного класу може змінюватися в різних потоках.
Ще одна відмінність полягає в тому, що StringBuffer займає трохи більше пам'яті, ніж StringBuilder, оскільки включає додаткові дані синхронізації. Однак у більшості додатків ця різниця досить мала, щоб нею можна було знехтувати.
Ееексперименти
Проведемо виміри, щоб подивитися наскільки використання StringBuilder та StringBuffer ефективніше.
Наш тест буде досить простим:
- Вимірюємо час старту.
- Беремо рядок і додаємо до нього рандомний рядок.
- Повторюємо 1_000_000 разів.
- Вимірюємо час закінчення.
Код для демо
package dev.struchkov.workshop.six.strings; import org.apache.commons.lang3.RandomStringUtils; public class Main < public static void main(String[] args) < long stringTime = string(); long stringBufferTime = stringBuffer(); long stringBuilder = stringBuilder(); System.out.println(); System.out.println("String:" + stringTime); System.out.println("StringBuffer:" + stringBufferTime); System.out.println("StringBuilder:" + stringBuilder); >private static long string() < final long startTime = System.currentTimeMillis(); String string = "Test"; for (int i = 0; i < 1_000_000; i++) < string += RandomStringUtils.randomAlphabetic(10); >final long finishTime = System.currentTimeMillis(); return finishTime - startTime; > private static long stringBuffer() < final long startTime = System.currentTimeMillis(); StringBuffer string = New StringBuffer("Test"); for (int i = 0; i < 1_000_000; i++) < string.append(RandomStringUtils.randomAlphabetic(10)); >final long finishTime = System.currentTimeMillis(); return finishTime - startTime; > private static long stringBuilder() < final long startTime = System.currentTimeMillis(); StringBuilder string = New StringBuilder("Test"); for (int i = 0; i < 1_000_000; i++) < string.append(RandomStringUtils.randomAlphabetic(10)); >final long finishTime = System.currentTimeMillis(); return finishTime - startTime; > >
- String дуже сумні: 3,6 хвилини
- StringBuilder: 236 мілісекнд.
- StringBuffer: 239 мілісекунд.
Резюмую
Насамкінець, основна відмінність між StringBuilder і StringBuffer в Java полягає в тому, що StringBuffer є потокобезпечним і синхронізованим, а StringBuilder - ні.
Вибір між цими двома класами повинен бути заснований на тому, чи є безпека потоків проблемою, а також на конкретних вимогах до продуктивності програми. Обидва класи є потужними інструментами для роботи з рядками і можуть використовуватися як взаємозамінні у багатьох випадках.
У чому різниця між String, StringBuffer та StringBuilder?
Рядки в Java реалізовані у вигляді об'єктів класу StringВони фіналізовані та незмінні, внаслідок цього при будь-яких маніпуляціях з ними завжди створюється новий рядок, що робить роботу з рядками дуже ресурсоємним процесом. StringBuffer і StringBuilder.
Природно виникають питання:
- У чому різниця між StringBuffer і StringBuilder?
- Що з-поміж них спільного?
- Коли краще використовувати StringBuffer , а коли StringBuilder?
Як уже було сказано, рядки в Java незмінні та фіналізовані. У разі необхідності, маніпуляції з рядками в Java забезпечуються двома класами – StringBuffer і StringBuilder.
StringBuffer і StringBuilder реалізують рядки, що змінюються, а відмінності між ними полягає в потокобезпеці та продуктивності.
Операції зі StringBuffer, на відміну від StringBuilder, потокобезпечні та синхронізовані. Тому у випадках, коли кілька потоків повинні працювати з одним рядком, то необхідно використовувати. StringBuffer. Однак у однопотокових оточеннях бажано використовувати StringBuilder, оскільки перевага StringBuilder перед StringBuffer полягає у продуктивності через відсутність витрат для підтримки синхронізації.
- String - Незмінний рядок.
- StringBuffer — потокобезпечний рядок, що змінюється.
- StringBuilder – рядок, що змінюється, з високою продуктивністю, але без синхронізації.