Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык программирования JAVA.pdf
Скачиваний:
374
Добавлен:
02.05.2014
Размер:
2.57 Mб
Скачать

converted to PDF by BoJIoc

Оба конструктора String, создающих строки по содержимому массива byte, производят копии данных, так что дальнейшие изменения содержимого массива никак не отражаются на содержимом строки.

8.8. Класс StringBuffer

Если бы программисты могли работать исключительно со строками, доступными только для чтения, приходилось бы создавать новый объект String для каждого промежуточного результата при последовательных операциях со строками. Например, давайте задумаемся над тем, как компилятор будет вычислять следующее выражение:

public static String guillemete(String quote) { return '"' + quote + '"';

}

Если бы компилятор ограничивался выражениями типа String, он действовал бы следующим образом:

quoted = String.valueOf('"').concat(quote)

.concat(String.valueOf('"'));

При каждом вызове valueOf и concat создается новый объект String; следовательно, в результате подобной операции появятся четыре объекта String, из которых в дальнейшем будет использован только один. Что касается остальных, то их создание, присвоение начальных значений и удаление будет сопряжено с непроизводительными расходами.

Разумеется, компилятор действует значительно эффективнее. Для построения промежуточных строк используются объекты класса StringBuffer, а итоговые объекты String создаются лишь в случае необходимости. Объекты StringBuffer могут изменяться, поэтому для хранения промежуточных результатов не нужны новые объекты. При помощи StringBuffer приведенное выше выражение может быть представлено в следующем виде:

quoted = new StringBuffer().append('"').

.append(quote).append('"').toString();

В этом варианте создается всего один объект StringBuffer, который хранит строку, добавляет к ней новые фрагменты и пользуется методом toString для преобразования результатов своей работы в объект String.

Для построения и модификации строки можно воспользоваться классом StringBuffer. Класс StringBuffer содержит следующие конструкторы:

public StringBuffer()

Конструирует новый объект StringBuffer, начальное значение которого равно “”.

public StringBuffer(String str)

Конструирует новый объект StringBuffer, исходное значение которого совпадает с str.

Класс StringBuffer во многих отношениях напоминает класс String, а многие из содержащихся в нем методов имеют те же имена и контракты, что и методы String. Тем не менее StringBuffer не является расширением String, и наоборот. Оба этих класса являются независимыми расширениями класса Object.

8.8.1. Модификация буфера

Существует несколько возможностей изменить содержимое буфера объекта StringBuffer, в том числе добавить новые символы в его конец или вставить их в середину. Самый простой из таких методов называется setCharAt и служить для замены символа в конкретной позиции. Также имеется метод replace, который делает то же самое, что и

converted to PDF by BoJIoc

String.replace, однако работает с объектом StringBuffer. Метод replace не нуждается в создании нового объекта для хранения результата, поэтому несколько последовательных вызовов replace могут выполняться с одним буфером:

public static void

replace(StringBuffer str, char from, char to)

{

for (int i = 0; i << str.length(); i++) if (str.charAt(i) == from)

str.setCharAt(i, to);

}

Метод setLength обрезает или расширяет строку, хранящуюся в буфере. Если передать setLength величину, меньшую длины текущей строки, то строка будет обрезана до указанного значения. Если же передаваемая длина превышает текущую, то строка расширяется, а новые позиции заполняются нуль-символами (\u0000).

Кроме того, имеются методы append и insert, которые преобразуют данные любого типа в String, после чего присоединяют результат преобразования к концу строки либо вставляют его в середину. При выполнении метода insert существующие символы сдвигаются, чтобы освободить место для вставляемых новых символов. Методы append и insert преобразуют данные следующих типов:

Object

String

char[]

boolean

char int

int

long

float

double

Также существуют методы append и insert, которым в качестве аргумента передается часть массива char. Например, для создания объекта String Buffer с описанием квадратного корня из целого числа можно написать следующее:

String sqrtInt(int i) {

StringBuffer buf = new StringBuffer();

buf.append("sqrt(").append(i).append(')'); buf.append(" = ").append(Math.sqrt(i)); return buf.toString();

}

Методы append и insert возвращают исходный объект String, что в данном случае позволяет нам добавить новые символы к результату предыдущего действия.

Метод insert получает два параметра. Первый из них позиция, начиная с которой в StringBuffer будут вставляться новые символы. Второй параметр вставляемое значение, при необходимости преобразованное в String. Приведем метод, который вставляет добавляет дату в начало буфера:

public static StringBuffer addDate(StringBuffer buf) { String now = new java.util.Date().toString(); buf.ensureCapacity(buf.length() + now.length() + 2); buf.insert(0, now).insert(now.length(), ": "); return buf;

}

Все начинается с создания строки, содержащей текущую дату. Для этой цели используется класс java.util.Date; его конструктор по умолчанию создает объект, представляющий текущее время на момент создания. Далее необходимо убедиться, что размеров буфера хватит для хранения всех добавляемых символов увеличение буфера должно происходить только один раз, а не после каждого вызова insert. Затем в буфер вставляется строка с текущим временем, за которой следует простейшая строка- разделитель. Наконец, переданный буфер возвращается обратно, чтобы при вызове данного метода можно было осуществить что-нибудь наподобие той конкатенации, которая пригодилась при работе с собственными методами StringBuffer.

converted to PDF by BoJIoc

Метод reverse изменяет порядок следования символов в StringBuffer. Например, если буфер содержит строку “good”, то после выполнения reverse в нем окажется строка

“doog”.

8.8.2. Извлечение данных

Чтобы создать объект String на основе объекта StringBuffer, следует вызвать метод toString.

В StringBuffer не существует методов, которые бы удаляли часть содержимого буфера вам придется преобразовать буфер в символьный массив, удалить то, что нужно, и построить новый буфер по массиву с оставшимися символами. Вероятно, для этого следует воспользоваться методом getChars, который аналогичен методу String.getChars.

public void getChars(int srcBegin, int srcEnd, char[] dst, int

dstBegin)

Копирует символы из заданной части буфера (определяемой позициями srcBegin и srcEnd) в массив dst начиная с dst[dstBegin]. Копирование ведется с позиции srcBegin до srcEnd (но не включает ее!). Позиция srcBegin должна представлять собой допустимый индекс для данного буфера, а позиция srcEnd не может превышать длины текущей строки в буфере (которая на единицу больше, чем последний индекс). Если какой-либо из индексов окажется недопустимым, возбуждается исключение Inde x OutOfBoundsException.

Приведем метод, в котором getChars используется для удаления части содержимого буфера:

public static StringBuffer

remove(StringBuffer buf, int pos, int cnt)

{

if (pos << 0 || cnt << 0 || pos + cnt >> buf.length()) throw new IndexOutOfBoundsException();

int leftover = buf.length() - (pos + cnt);

if (leftover == 0) { // простое обрезание строки buf.setlength(pos);

return buf;

}

char[] chrs = new char[leftover]; buf.getChars(pos + cnt, buf.Length(), chrs, 0); buf.setLength(pos);

buf.append(chrs); return buf;

}

Прежде всего необходимо убедиться в том, что значения индексов массива не выходят за пределы допустимых. Обработать исключение можно и позднее, однако немедленная проверка позволяет лучше контролировать ситуацию. Затем мы вычисляем, сколько элементов находится после удаляемой части массива; если таких элементов нет, обрезаем буфер и возвращаемся. В противном случае, перед тем как возвращаться из метода, необходимо выделить эти символы методом getChars, затем обрезать буфер и приписать к нему выделенный ранее остаток.

8.8.3. Работа с емкостью буфера

Буфер объекта StringBuffer обладает определенной емкостью так называется максимальная длина строки, которая может поместиться в нем перед тем, как придется

converted to PDF by BoJIoc

выделять дополнительное место. Буфер может увеличиваться автоматически по мере добавления новых символов, однако для повышения эффективности желательно установить его размер один раз.

Исходный размер буфера объекта StringBuffer может быть задан с помощью конструктора, получающего всего один параметр типа int:

public StringBuffer(int capacity)

Конструирует новый объект StringBuffer с заданной исходной емкостью и начальным значением “”.

public synchronized void ensureCapacity(int minimum)

Позволяет убедиться в том, что буфер имеет емкость не менее заданного minimum.

public int capacity()

Возвращает текущую емкость буфера.

С помощью этих методов можно избежать многократного увеличения буфера. Ниже приводится новая версия метода sqrtInt, которая обеспечивает не более чем однократное выделение нового пространства для буфера:

String sqrtIntFaster(int i) {

StringBuffer buf = new StringBuffer(50); buf.append("sqrt(").append(i).append(')'); buf.append(" = ").append(Math.sqrt(i)); return buf.toString();

}

Единственное изменение заключается в том, что на этот раз используется конструктор, который создает достаточно большой объект StringBuffer, способный вместить строку с результатом. Значение 50 несколько превышает максимально необходимое; следовательно, буфер никогда не придется увеличивать заново.

Упражнение 8.4

Напишите метод для преобразования строк с десятичными числами, при котором после каждой третьей цифры справа ставится запятая. Например, для исходной строки “1542729" метод должен возвращать строку ”1,542,729".

Упражнение 8.5

Измените метод из предыдущего упражнения так, чтобы при его вызове можно было указать символ-разделитель и количество цифр между разделителями.