1. Клас StringTokenizer

Є ще кілька сценаріїв, які дуже часто використовуються для роботи з рядками. Як розділити рядок на частини? Для цього є декілька способів.

Метод split()

Найпростіший спосіб розділити рядок на кілька частин — це скористатися методом split(). У нього потрібно передати як параметр регулярний вираз: спеціальний шаблон рядка-роздільника. Що таке регулярний вираз, ви дізнаєтеся у квесті Java Multithreading.

Приклад:

Код Результат
String str = "Good news everyone!";
String[] strings = str.split("ne");
System.out.println(Arrays.toString(strings));
Результатом буде масив із трьох рядків:
["Good ", "ws everyo", "!"]

Це простий спосіб, але часом такий підхід є надмірним. Якщо роздільників багато, наприклад «пробіл», «enter», «tab», «крапка», доводиться конструювати досить складний регулярний вираз. Його складно читати, отож у нього складно вносити зміни.

Клас StringTokenizer

У мові Java є спеціальний клас, призначений саме для розділення рядків на підрядки.

Цей клас не використовує регулярні вирази, натомість в нього просто передається рядок, який складається із символів-роздільників. Переваги цього підходу в тому, що рядок не відразу розбивається на шматочки, а поступово аналізується від початку до кінця.

Клас складається з конструктора і двох методів. У конструктор передається рядок, який потрібно розбити на частини, і рядок — набір символів, які використовуються як роздільники.

Методи Опис
String nextToken()
Повертає наступний підрядок
boolean hasMoreTokens()
Перевіряє, чи немає ще підрядків.

Цей клас трохи нагадує клас Scanner, який теж має методи nextLine() і hasNextLine().

Для створення об'єкта StringTokenizer використовують таку команду:

StringTokenizer ім'я = new StringTokenizer(рядок, роздільники);

де рядок — це рядок, який потрібно розділити на частини; а роздільники — це рядок, кожний символ в якому вважається символом-роздільником. Приклад:

Код Виведення на екран
String str = "Good news everyone!";

StringTokenizer tokenizer = new StringTokenizer(str,"ne");
while (tokenizer.hasMoreTokens())
{
   String token = tokenizer.nextToken();
   System.out.println(token);
}
Good 
ws 
v
ryo
!

Зауважте, що кожен символ у другому рядку, переданому в конструктор StringTokenizer, вважається роздільником.



2. Метод String.format() і клас StringFormatter

Клас String має ще один цікавий метод — format().

Припустімо, у вас є різні змінні з даними. Як вивести їх на екран одним рядком? Наприклад, маємо дані (лівий стовпець), які ми хочемо вивести на екран у певному вигляді (правий стовпець):

Код Виведення на екран
String name = "Amigo";
int age = 12;
String friend = "Diego";
int weight = 200;
User = {name: Amigo, age: 12 years, friend: Diego, weight: 200 kg.}

Ваш код матиме приблизно такий вигляд:

Код програми
String name = "Amigo";
int age = 12;
String friend = "Diego";
int weight = 200;

System.out.println("User = {name: " + name + ", age:" + age + " years, friend: " + friend+", weight: " + weight + " kg.}");

Такий код не дуже зручно читати. А якби імена змінних були довшими, код став би ще складнішим:

Код програми
String name = "Amigo";
int age = 12;
String friend = "Diego";
int weight = 200;

System.out.println("User = {name: " + user.getName() + ", age:" + user.getAge() + " years, friend: " + user.getFriends().get(0) + ", weight: " + user.getExtraInformation().getWeight() + " kg.}");

Не дуже зручно читати, чи не так?

Проте в реальних програмах така ситуація трапляється досить часто, тому я розповім, як спростити й скоротити цей код.

String.format

Клас String має статичний метод format(): він дає змогу визначити шаблон об'єднання рядка з даними. Загальний вигляд цієї команди такий:

String ім'я = String.format(шаблон, параметри);

Приклад:

Код Результат
String.format("Age=%d, Name=%s", age, name);
Age=12, Name=Amigo
String.format("Width=%d, Height=%d", width, height);
Width=20, Height=10
String.format("Fullname=%s", name);
Fullname=Diego

У метод format() першим параметром передається рядок-шаблон, який містить увесь потрібний текст, а в ті місця, куди потрібно вставити дані, вписано спеціальні символи типу %d, %s тощо.

Метод format() замінює ці символи %s і %d на параметри, вказані після рядка-шаблону. Коли потрібно підставити рядок, пишемо %s, а коли число — %d. Приклад:

Код Результат
String s = String.format("a=%d, b=%d, c=%d", 1, 4, 3);
s дорівнюватиме "a=1, b=4, c=3"

Скорочений перелік параметрів, які можна використовувати в шаблоні:

Символ Позначення
%s
String
%d
ціле число: byte, short, int, long
%f
дійсне число: float, double
%b
boolean
%c
char
%t
Date
%%
Символ %

Ці параметри вказують на тип даних, але є такі параметри, які вказують на порядок даних. Для того щоб вибрати параметр за номером (нумерація починається з одиниці), потрібно написати %1$d замість %d. Приклад:

Код Результат
String s = String.format("a=%3$d, b=%2$d, c=%d", 11, 12, 13);
s дорівнюватиме "a=13, b=12, c=11"

Замість %3$d підставиться 3-й параметр-змінна, замість %2$d — 2-й параметр-змінна, а замість %d — найперший параметр-змінна. Параметри шаблону %s, %d звертаються до параметрів-змінних незалежно від параметрів шаблону типу %3$d або %2$s.



3. String Pool

Усі рядки, що задано в коді як літерали, під час роботи програми зберігаються в пам'яті в особливому місці, яке має назву StringPool. StringPool — це спеціальний простір, створений для оптимізації зберігання рядків.

По-перше, рядки, визначені в коді, потрібно десь зберігати. Код — це команди, а дані (зокрема такі великі, як рядки) потрібно зберігати в пам'яті окремо від коду. У коді використовуються тільки посилання на об'єкти-рядки.

По-друге, можна усі однакові літерали зберегти в пам'яті лише один раз. Отак воно й працює. Коли код вашого класу завантажується Java-машиною, усі рядкові літерали додаються до StringPool (якщо їх там ще немає). Якщо такий рядок у StringPool вже є, просто використовується посилання на цей рядок.

Тому, якщо в коді присвоїти декільком змінним типу String однакові літерали, ці змінні будуть містити однакові посилання. У StringPool літерал додається тільки один раз, у решті випадків буде використовуватися посилання на рядок, уже завантажений у StringPool.

Отак це працює:

Код Робота зі StringPool
String a = "Привіт";
String b = "Привіт";
String c = "Бувай";
String[] pool = {"Привіт", "Бувай"};
a = pool[0];
b = pool[0];
c = pool[1];

Саме тому змінні a і b зберігатимуть однакові посилання.

Метод intern()

І найцікавіше: будь-який рядок можна програмним шляхом додати до StringPool. Для цього потрібно просто викликати для змінної типу String метод intern().

Метод intern() додасть рядок до StringPool, якщо його там ще немає, і поверне посилання на рядок зі StringPool.

Якщо за допомогою методу intern() додати до StringPool два ідентичних рядки, метод поверне однакові посилання. Цим можна користуватися для порівняння рядків за посиланнями. Приклад:

Код Примітка
String a = new String("Привіт");
String b = new String("Привіт");
System.out.println(a == b);


false
String a = new String("Привіт");
String b = new String("Привіт");

String t1 = a.intern();
String t2 = b.intern();
System.out.println(a == b);
System.out.println(t1 == t2);





false
true

Навряд чи ви часто використовуватимете цей метод, проте про нього зазвичай запитують на співбесідах, тож його варто знати.