1. Незмінність рядка: друг чи ворог?
У Java клас String — незмінний (immutable). Це означає, що після створення рядок не можна змінити. Щоразу, коли ви «змінюєте» рядок, наприклад, додаєте до нього щось через + або concat(), насправді створюється новий обʼєкт, а старий вирушає на звалище (поки його не прибере збирач сміття).
Приклад:
String s = "Привіт";
s = s + " світ!";
System.out.println(s); // Привіт світ!
Здається, що рядок s змінився, але насправді створився новий рядок "Привіт світ!", а старий "Привіт" залишився в памʼяті, доки його не прибере збирач сміття. Якщо таких операцій багато — наприклад, у циклі, — програма сповільнюється і споживає зайву памʼять.
Уявіть, що ви будуєте вежу з кубиків і щоразу, коли хочете додати новий кубик, доводиться перебудовувати всю вежу заново. Не дуже економно, правда? Так само поводиться звичайний String у Java під час частих змін.
2. StringBuilder: швидкий «будівничий» рядків
Клас StringBuilder (пакет java.lang — імпортувати не потрібно) — це спеціальний інструмент для ефективного збирання та зміни рядків. Він змінюваний (mutable): можна додавати, видаляти, вставляти символи та підрядки без створення нового обʼєкта на кожну операцію.
Аналогія:
Якщо String — це бетонна плита, то StringBuilder — конструктор LEGO: додаєте й прибираєте деталі скільки завгодно, не розбираючи все заново.
Як створити StringBuilder
StringBuilder sb = new StringBuilder(); // порожній
StringBuilder sb2 = new StringBuilder("Початкове значення");
Основні методи
| Метод | Опис | Приклад використання |
|---|---|---|
|
Додати у кінець рядок, число, символ тощо. | |
|
Вставити значення у зазначену позицію. | |
|
Видалити символи з позиції start (включно) до end (невключно). | |
|
Замінити частину рядка іншим вмістом. | |
|
Розвернути рядок задом наперед. | |
|
Перетворити на звичайний рядок. | |
|
Обрізати або доповнити рядок до заданої довжини. | |
Приклад використання
StringBuilder sb = new StringBuilder();
sb.append("Привіт, ");
sb.append("світ!");
System.out.println(sb); // Привіт, світ!
sb.insert(7, "Java "); // вставимо "Java " після "Привіт, "
System.out.println(sb); // Привіт, Java світ!
sb.replace(8, 12, "інший"); // замінимо "Java" на "інший"
System.out.println(sb); // Привіт, інший світ!
sb.reverse();
System.out.println(sb); // !тівс йишні ,тівирП
3. StringBuffer: старший брат із потокобезпекою
У чому різниця між StringBuilder і StringBuffer?
- StringBuilder — швидший, але непотокобезпечний (несинхронізований).
- StringBuffer — повільніший, але потокобезпечний (синхронізований).
Якщо ваш застосунок працює в одному потоці — використовуйте StringBuilder (швидше). Якщо кілька потоків можуть змінювати один і той самий рядок — використовуйте StringBuffer.
4. Коли використовувати StringBuilder замість String?
Сценарії
- Часті зміни рядка (додавання, видалення, вставка) у циклі або під час збирання великих текстів.
- Збирання рядка з масиву або списку (CSV, HTML, звіти тощо).
- Розбір і обробка тексту з великою кількістю операцій над рядком.
Приклад: збирання рядка з масиву
Поганий спосіб (через String і +):
String[] names = {"Іван", "Петро", "Марія"};
String result = "";
for (int i = 0; i < names.length; i++)
{
result += names[i];
if (i < names.length - 1)
{
result += ", ";
}
}
System.out.println(result);
Хороший спосіб (через StringBuilder):
String[] names = {"Іван", "Петро", "Марія"};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < names.length; i++)
{
sb.append(names[i]);
if (i < names.length - 1)
{
sb.append(", ");
}
}
System.out.println(sb.toString());
Різниця: у першому випадку на кожному кроці створюється новий рядок, у другому — ви нарощуєте один обʼєкт StringBuilder.
5. Порівняння продуктивності: String vs StringBuilder
// Через String
long t1 = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 10000; i++)
{
s += i + " ";
}
long t2 = System.currentTimeMillis();
System.out.println("String: " + (t2 - t1) + " мс");
// Через StringBuilder
t1 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
sb.append(i).append(" ");
}
s = sb.toString();
t2 = System.currentTimeMillis();
System.out.println("StringBuilder: " + (t2 - t1) + " мс");
Підсумок: у більшості випадків StringBuilder працює значно швидше під час множинних конкатенацій.
6. Корисні нюанси
Чи можна використовувати методи String?
StringBuilder має власні методи. Щоб отримати рядок, викликайте toString().
StringBuilder sb = new StringBuilder("Привіт");
String s = sb.toString(); // тепер s — звичайний рядок
Чи можна порівнювати StringBuilder через equals?
Увага: sb1.equals(sb2) порівнює посилання, а не вміст. Порівнюйте так:
if (sb1.toString().equals(sb2.toString()))
{
// вміст збігається
}
Чи можна передати StringBuilder у System.out.println?
Так. System.out.println автоматично викличе toString().
Чи можна читати символи за індексом?
Так, використовуйте charAt(int index), як у звичайного рядка.
7. Типові помилки під час роботи з StringBuilder і StringBuffer
Помилка № 1: порівнювати два StringBuilder через equals або оператор ==. Ці перевірки порівнюють посилання, а не вміст. Використовуйте toString() і порівняння рядків.
Помилка № 2: забувати викликати toString() там, де очікується String (повернення з методу, логування, передавання в API).
Помилка № 3: застосовувати StringBuilder для кількох простих конкатенацій. Запис виду "Hello, " + name нормально читається й достатньо ефективний.
Помилка № 4: конкатенувати String у циклі через +. Це неефективно за часом і памʼяттю — використовуйте StringBuilder.
Помилка № 5: плутати StringBuffer і StringBuilder без потреби. Якщо немає спільної модифікації з кількох потоків — обирайте StringBuilder.
Помилка № 6: бездумно обрізати вміст StringBuilder методом setLength(). Перевіряйте коректність нової довжини — дані після неї будуть втрачені.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ