Глава 7
СТРОКИ
Строка — это застывшая структура данных, и по-
всюду, куда она передается, происходит значительное ду-
блирование процесса. Это идеальное средство для сокры-
тия информации.
Алан Дж. Перлис
Строка в языке Java — это основной носитель текстовой информации. Это
не массив символов типа char, а объект соответствующего класса. Системная
библиотека Java содержит классы String, StringBuilder и StringBuffer, под-
держивающие работу со строками и определенные в пакете java.lang, подклю-
чаемом автоматически. Эти классы объявлены как final, что означает невоз-
можность создания собственных порожденных классов со свойствами строки.
Для форматирования и обработки строк применяются классы Formatter,
Pattern, Matcher и другие.
Класс String
Каждая строка, создаваемая с помощью оператора new или с помощью ли-
терала (заключенная в двойные апострофы), является экземпляром класса
String. Особенностью объекта класса String является то, что его значение не
может быть изменено после создания объекта при помощи какого-либо метода
класса, так как любое изменение строки приводит к созданию нового объекта.
Класс String поддерживает несколько конструкторов, например: String(),
String(String str), String(byte[] asciichar), String(char[] unicodechar),
String(StringBuffer sbuf), String(StringBuilder sbuild) и др. Эти конструкторы
используются для создания объектов класса String на основе инициализации
значениями из массива типа char, byte и др. Например, при вызове конструктора
new String(str.getBytes(),
"UTF-8")
можно установить кодировку создаваемому экземпляру в качестве второго
параметра конструктора. Когда Java встречает литерал, заключенный в двойные
кавычки, автоматически создается объект-литерал типа String, на который мож-
но установить ссылку. Таким образом, объект класса String можно создать, при-
своив ссылке на класс значение существующего литерала или с помощью опера-
тора new и конструктора, например:
СТРОКИ
171
String s1 = "oracle.com";
String s2 = new String("oracle.com");
Класс String содержит следующие методы для работы со строками:
String concat(String s) или « +» — слияние строк;
boolean equals(Object ob) и equalsIgnoreCase(String s) — сравнение строк
с учетом и без учета регистра соответственно;
int compareTo(String s) и compareToIgnoreCase(String s) — лексикографи-
ческое сравнение строк с учетом и без учета их регистра. Метод осуществляет
вычитание кодов первых различных символов вызывающей и передаваемой
строки в метод строк и возвращает целое значение. Метод возвращает значе-
ние нуль в случае, когда equals() возвращает значение true;
boolean contentEquals(StringBuffer ob) — сравнение строки и содержимо-
го объекта типа StringBuffer;
String substring(int n, int m) — извлечение из строки подстроки длины
m-n, начиная с позиции n. Нумерация символов в строке начинается с нуля;
String substring(int n) — извлечение из строки подстроки, начиная с позиции n;
int length() — определение длины строки;
int indexOf(char ch) — определение позиции символа в строке;
static String valueOf(значение) — преобразование переменной базового
типа к строке;
String toUpperCase()/toLowerCase() — преобразование всех символов вы-
зывающей строки в верхний/нижний регистр;
String replace(char с1, char с2) — замена в строке всех вхождений первого
символа вторым символом;
String intern() — заносит строку в «пул» литералов и возвращает ее объект-
ную ссылку;
String trim() — удаление всех пробелов в начале и конце строки;
char charAt(int position) — возвращение символа из указанной позиции
(нумерация с нуля);
boolean isEmpty() — возвращает true, если длина строки равна 0;
char[] getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) — извле-
чение символов строки в массив символов;
static String format(String format, Object… args), format(Locale l, String
format, Object… args) — генерирует форматированную строку, полученную
с использованием формата, интернационализации и др.;
String[] split(String regex), String[] split(String regex, int limit) — поиск
вхождения в строку заданного регулярного выражения (разделителя) и деле-
ние исходной строки в соответствии с этим на массив строк.
Во всех случаях вызова методов, изменяющих строку, создается новый объ-
ект типа String.
Эффективной демонстрацией работы методов класса служит преобразование
строки в массив объектов типа String и их сортировка в алфавитном порядке.
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
172
Ниже рассмотрена сортировка массива строк методом выбора, таким обра-
зом демонстрируются возможности методов класса.
// # 1 # сортировка # SortArray.java
package
by.bsu.strings;
public
class SortArray {
public
static void main(String[ ] args) {
String names = " Alena Alice alina albina Anastasya ALLA ArinA ";
names = names.trim(); // удаление пробелов по краям строки
// разбиение строки на подстроки, где " пробел" — разделитель
String a[ ] = names.split(" "); // массив содержит элементы нулевой длины
for
( int j = 0; j < a.length — 1; j++) {
for
( int i = j + 1; i < a.length; i++) {
if
(a[i].compareToIgnoreCase(a[j]) < 0) {
String temp = a[j];
a[j]
=
a[i];
a[i]
=
temp;
}
}
}
for
(String arg : a) {
if
(!arg.isEmpty()) {
System. out.print(arg + " ");
}
}
}
}
albina Alena Alice alina ALLA Anastasya ArinA
Вызов метода trim() обеспечивает удаление всех начальных и конечных
символов пробелов. Метод compareToIgnoreCase() выполняет лексикогра-
фическое сравнение строк между собой по правилам Unicode. Оператор
if(!arg.isEmpty()) не позволяет выводить пустые строки.
При использовании методов класса String, изменяющих строку, создается
новый обновленный объект класса String. Сохранить произведенные измене-
ния экземпляра класса String можно только с применением оператора присваи-
вания, т. е. установкой ссылки на вновь созданный объект. В следующем при-
мере будет подтвержден тезис о неизменяемости экземпляра типа String.
/* # 2 # передача строки по ссылке # RefString.java */
package
by.bsu.strings;
public
class RefString {
public
static void changeStr(String s) {
s = s.concat(" Certified"); // создается новая строка
//
или s.concat(" Certified");
// или s += " Certified";
СТРОКИ
173
}
public
static void main(String[ ] args) {
String str = new String("Java");
changeStr(str);
System. out.print(str);
}
}
В результате будет выведена строка:
Java
Поскольку объект был передан по ссылке, любое изменение объекта в мето-
де должно сохраняться и для исходного объекта, так как обе ссылки равноправ-
ны. Это не происходит по той причине, что вызов метода concat(String s) при-
водит к созданию нового объекта.
При создании экземпляра класса String путем присваивания его ссылки
на литерал, последний помещается в так называемый «пул литералов». Если
в дальнейшем будет создана еще одна ссылка на литерал, эквивалентный ранее
объявленному, то будет произведена попытка добавления его в «пул литера-
лов». Так как идентичный литерал там уже существует, то дубликат не может
быть размещен, и вторая ссылка будет на существующий литерал. Аналогично
в случае, если литерал является вычисляемым. То есть компилятор восприни-
мает литералы "Java" и "J" + "ava" как эквивалентные.
// # 3 # сравнение ссылок и объектов # EqualStrings.java
package
by.bsu.strings;
public
class EqualStrings {
public
static void main(String[ ] args) {
String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");
String s4 = new String(s1);
System. out.println(s1 + "==" + s2 + " : " + ( s1 == s2)); // true
System. out.println(s3 + "==" + s4 + " : " + (s3 == s4)); // false
System. out.println(s1 + "==" + s3 + " : " + (s1 == s3)); // false
System.out.println(s1 + " equals " + s2 + " : " + s1.equals(s2)); // true
System.out.println(s1 + " equals " + s3 + " : " + s1.equals(s3)); // true
}
}
В результате, например, будет выведено:
Java==Java : true
Java==Java : false
Java==Java : false
Java equals Java : true
Java equals Java : true
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
174
Несмотря на то, что одинаковые по значению строковые объекты располо-
жены в различных участках памяти, значения их хэш-кодов совпадают.
Так как в Java все ссылки хранятся в стеке, а объекты — в куче, то при со-
здании объекта s2 сначала создается ссылка, а затем этой ссылке устанавлива-
ется в соответствие объект. В данной ситуации s2 ассоциируется с уже сущест-
вующим литералом, так как объект s1 уже сделал ссылку на этот литерал. При
создании s3 происходит вызов конструктора, т. е. выделение памяти происхо-
дит раньше инициализации, и в этом случае в куче создается новый объект.
Существует возможность сэкономить память и переопределить ссылку
с объекта на литерал при помощи вызова метода intern().
// # 4 # занесение в пул литералов # DemoIntern.java
package
by.bsu.strings;
public
class DemoIntern {
public
static void main(String[ ] args) {
String s1 = "Java"; // литерал и ссылка на него
String s2 = new String("Java");
System. out.println(s1 == s2); // false
s2 = s2.intern();
System. out.println(s1 == s2); // true
}
}
В данной ситуации ссылка s1 инициализируется литералом, обладающим
всеми свойствами объекта вплоть до вызова методов. Вызов метода intern()
организует поиск в «пуле литералов» соответствующего значению объекта s2
литерала и при положительном результате возвращает ссылку на найденный
литерал, а при отрицательном — заносит значение в пул и возвращает ссылку
на него.
Классы StringBuilder и StringBuffer
Классы StringBuilder и StringBuffer являются «близнецами» и по своему
предназначению близки к классу String, но в отличие от последнего содержи-
мое и размеры объектов классов StringBuilder и StringBuffer можно изменять.
Основным отличием StringBuilder от StringBuffer является потокобезопас-
ность последнего. Более высокая скорость обработки есть следствие отсутст-
вия потокобезопасности класса StringBuilder. Его следует применять, если не
существует вероятности использования объекта в конкурирующих потоках.
С помощью соответствующих методов и конструкторов объекты классов
StringBuffer, StringBuilder и String можно преобразовывать друг в друга.
Конструктор класса StringBuffer (также как и StringBuilder) может принимать
в качестве параметра объект String или неотрицательный размер буфера. Объекты
СТРОКИ
175
этого класса можно преобразовать в объект класса String методом toString() или
с помощью конструктора класса String.
Следует обратить внимание на следующие методы:
void setLength(int n) — установка размера буфера;
void ensureCapacity(int minimum) — установка гарантированного мини-
мального размера буфера;
int capacity() — возвращение текущего размера буфера;
StringBuffer append(параметры) — добавление к содержимому объекта
строкового представления аргумента, который может быть символом, значени-
ем базового типа, массивом и строкой;
StringBuffer insert(параметры) — вставка символа, объекта или строки
в указанную позицию;
StringBuffer deleteCharAt(int index) — удаление символа;
StringBuffer delete(int start, int end) — удаление подстроки;
StringBuffer reverse() — обращение содержимого объекта.
В классе присутствуют также методы, аналогичные методам класса String,
такие как replace(), substring(), charAt(), length(), getChars(), indexOf() и др.
/* # 5 # свойства объекта StringBuffer # DemoStringBuffer.java */
package
by.bsu.strings;
public
class DemoStringBuffer {
public
static void main(String[] args) {
StringBuffer sb = new StringBuffer();
System. out.println("длина —> " + sb.length());
System. out.println("размер —>" + sb.capacity());
// sb = " Java" ; // ошибка, только для класса String
sb.append("Java");
System. out.println("строка —> " + sb);
System. out.println("длина —> " + sb.length());
System. out.println("размер —> " + sb.capacity());
sb.append("Internationalization");
System. out.println("строка —> " + sb);
System. out.println("длина —> " + sb.length());
System. out.println("размер —> " + sb.capacity());
System. out.println("реверс —> " + sb.reverse());
}
}
Результатом выполнения данного кода будет:
длина —> 0
размер —> 16
строка —> Java
длина —> 4
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
176
размер —> 16
строка —> JavaInternationalization
длина —> 24
размер —> 34
реверс —> noitazilanoitanretnIavaJ
При создании объекта StringBuffer конструктор по умолчанию автоматиче-
ски резервирует некоторый объем памяти (16 символов), что в дальней шем по-
зволяет быстро менять содержимое объекта, оставаясь в границах участка памя-
ти, выделенного под объект. Размер резервируемой памяти при необходимости
можно указывать в конструкторе. Если длина строки StringBuffer после измене-
ния превышает его размер, то емкость объекта автоматически увеличивается,
оставляя при этом некоторый резерв для дальнейших изменений. Методом
reverse() можно быстро изменить порядок символов в объекте.
Если метод, вызываемый объектом StringBuilder, производит измене ния
в его содержимом, то это не приводит к созданию нового объекта, как в случае
объекта String, а изменяет текущий объект StringBuilder.
/* # 6 # изменение объекта StringBuilder # RefStringBuilder.java */
package
by.bsu.strings;
public
class RefStringBuilder {
public
static void changeStr(StringBuilder s) {
s.append("
Certified");
}
public
static void main(String[ ] args) {
StringBuilder str = new StringBuilder("Oracle");
changeStr(str);
System.out.println(str);
}
}
В результате выполнения этого кода будет выведена строка:
Oracle Certified
Объект
StringBuilder передан в метод changeStr() по ссылке, поэтому все
изменения объекта сохраняются и для вызывающего метода.
Для классов StringBuffer и StringBuilder не переопределены методы equals()
и hashCode(), т. е. сравнить содержимое двух объектов невозможно, следова-
тельно хэш-коды всех объектов этого типа вычисляются так же, как и для клас-
са Object. При идентичном содержимом у двух экземпляров, размеры буфера
каждого могут отличаться, поэтому сравнение на эквивалентность объектов
представляется неоднозначным.
/* # 7 # сравнение объектов StringBuffer и их хэш-кодов # EqualsStringBuffer.java */
package
by.bsu.strings;
public
class EqualsStringBuffer {
СТРОКИ
177
public
static void main(String[ ] args) {
StringBuffer sb1 = new StringBuffer();
StringBuffer sb2 = new StringBuffer(48);
sb1.append("Java");
sb2.append("Java");
System. out.print(sb1.equals(sb2));
System. out.print(sb1.hashCode() == sb2.hashCode());
}
}
Результатом выполнения данной программы будет дважды выведенное зна-
чение false.
Сравнить содержимое можно следующим образом:
sb1.toString().contentEquals(sb2)
Регулярные выражения
Класс java.util.regex.Pattern применяется для определения регулярных
выражений (шаблонов), для которых ищется соответствие в строке, файле
или другом объекте, представляющем последовательность символов. Для оп-
ределения шаблона применяются специальные синтаксические конструкции.
О каждом соответствии можно получить информацию с помощью класса
java.util.regex.Matcher.
Далее приведены некоторые логические конструкции для задания шаблона.
Если необходимо, чтобы в строке, проверяемой на соответствие, в какой-либо по-
зиции находился один из символов некоторого символьного набора, то такой на бор
(класс символов) можно объявить, используя одну из следующих конструкций:
[abc]
a, b или c
[^abc]
символ, исключая a, b и c
[a-z]
символ между a и z
[a-d[m-p]]
между a и d, или между m и p
Кроме стандартных классов символов существуют предопределенные клас-
сы символов:
.
любой символ
\
d или \p{Digit}
[ 0-9]
\
D
[ ^0-9]
\
s или \p{Space}
[ \t\n\x0B\f\r]
\S
[ ^\s]
\w
[ 0-9_A-Za-z]
\W
[ ^\w]
ИСПОЛЬЗОВАНИЕ КЛАССОВ И БИБЛИОТЕК
178
\p{Lower}
[ a-z]
\p{Upper}
[ A-Z]
\p{Punkt}
!"#$%& '()*+,-./:;<=>?@[\]^_`{|}~
\p{Blank}
Пробел или табуляция
При создании регулярного выражения могут использоваться логические
операции:
ab
после a следует b
a|b
a либо b
(a)
а
Скобки кроме их логического назначения также используются для выделе-
ния групп.
Для определения регулярных выражений недостаточно одних классов сим-
волов, т. к. в шаблоне часто нужно указать количество повторений. Для этого
существуют квантификаторы.
a?
a один раз или ни разу
a*
a ноль или более раз
a+
a один или более раз
a{n}
a n раз
a{n,}
a n или более раз
a{n,m}
a от n до m
Существует еще два типа квантификаторов, которые образованы прибавле-
нием суффикса «?» (слабое или неполное совпадение) или « +» («жадное» или
соб ственное совпадение) к вышеперечисленным квантификаторам. Неполное
совпадение соответствует выбору с наименее возможным количеством симво-
лов, а собственное — с максимально возможным.
Класс Pattern используется для простой обработки строк. Для более слож-
ной обработки строк используется класс Matcher, рассматриваемый ниже.
В классе Pattern объявлены следующие методы:
Pattern compile(String regex) — возвращает Pattern, который соответству-
ет regex;
boolean matches(String regex, CharSequence input) — проверяет на соот-
ветствие строки input шаблону regex;
String[] split(CharSequence input) — разбивает строку input, учитывая, что
разделителем является шаблон;
Matcher matcher(CharSequence input) — возвращает Matcher, с помощью
которого можно находить соответствия в строке input.
СТРОКИ
179
С помощью метода matches() класса Pattern можно проверять на соответ-
ствие шаблону целую строку, но если необходимо найти соответствия внутри
строки, например, определять участки, которые соответствуют шаблону, то
класс Pattern не может быть использован. Для таких операций необходимо
использовать класс Matcher.
Начальное состояние объекта типа Matcher не определено. Попытка выз-
вать какой-либо метод класса для извлечения информации о найденном соот-
ветствии приведет к возникновению ошибки IllegalStateException. Для того,
чтобы начать работу с объектом Matcher, нужно вызвать один из его методов:
boolean matches() — проверяет, соответствует ли вся информация шаблону;
boolean lookingAt() — поиск последовательности символов, начинающейся
с начала строки и соответствующей шаблону;
boolean find() или boolean find(int start) — ищет последовательность сим-
волов, соответствующих шаблону, в любом месте строки. Параметр start ука-
зывает на начальную позицию поиска.
Иногда необходимо сбросить состояние экземпляра Matcher в исходное,
для этого применяется метод reset() или reset(CharSequence input), который
также устанавливает новую последовательность символов для поиска.
Для замены всех подпоследовательностей символов, удовлетворяющих шабло-
ну, на заданную строку можно применить метод replaceAll(String replacement).
В регулярном выражении для более удобной обработки входной после до ва-
тельности применяются группы, которые помогают выделить части найденной под-
последовательности. В шаблоне они обозначаются скобками « (» и « )». Номера групп
начинаются с единицы. Нулевая группа совпадает со всей найденной подпоследова-
тельностью. Далее приведены методы для извлечения информации о группах.
Достарыңызбен бөлісу: |