Всем привет! С момента выхода Java 11 прошли сутки, и вот уже наконец стали появляться первые обзоры релиза. Я же посвящу свою небольшую статью незаметному для официальных релизов и потому обделённому вниманием обновлению класса String, тем более, что оно не упоминается в официальной документации 11-й Java (я, во всяком случае, там информации об этом не нашёл).
Действительно, если мы заглянем в класс String, то среди множества знакомых нам методов мы найдём несколько, помеченных как "@since 11". И да, официально в Java они появились только вчера.
Конечно, в полезности каждой из функций вполне могут быть большие сомнения, поскольку самые полезные и необходимые функции уже были написаны в предыдущих версиях Java, но кому-то и эти пригодиться могут. Статья вышла небольшой, но в этом не только моя вина, но и вина Oracle — они включили в релиз всего 4 (+2) метода, что, конечно, немного.
Приступим.
strip();
Этот метод убирает все пробелы, находящиеся до первого не-пробела и после последнего. Например:
String withSpaces = " a ";
String withoutSpaces = withSpaces.strip();
String OUTPUT_TEMPLATE = "<%s>"
System.out.println(String.format(OUTPUT_TEMPLATE, withSpaces));
System.out.println(String.format(OUTPUT_TEMPLATE, withoutSpaces));
Результат, выведенный на экран, будет:
original: < a >
strip: <a>
У метода strip() есть два двоюродных брата — stripLeading() и stripTrailing(). Первый — убирает пробелы только спереди, перед первым не-пробелом. Второй — только сзади.
String leading = withSpaces.stripLeading();
String trailing = withSpaces.stripTrailing();
Получаем результат:
stripLeading: <a >
stripTrailing: < a>
UPD.
Тут в комментариях подсказывают, что не помешало бы посмотреть, какова разница с тем же методом trim(), который, по сути, делает то же самое.
Смотрим.
public static String trim(byte[] value) {
int len = value.length;
int st = 0;
while ((st < len) && ((value[st] & 0xff) <= ' ')) {
st++;
}
while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) {
len--;
}
return ((st > 0) || (len < value.length)) ?
newString(value, st, len - st) : null;
}
Как мы видим, в старой реализации при помощи двух итераций сначала вычисляется индекс первого не-пробела, а потом индекс последнего не-пробела, и потом по этим данным нарезается и возвращается новая строка. Отсекаются просто пробелы, заметим это.
Теперь смотрим на метод strip().
public static String strip(byte[] value) {
int left = indexOfNonWhitespace(value);
if (left == value.length) {
return "";
}
int right = lastIndexOfNonWhitespace(value);
return ((left > 0) || (right < value.length)) ? newString(value, left, right - left) : null;
}
public static int indexOfNonWhitespace(byte[] value) {
int length = value.length;
int left = 0;
while (left < length) {
char ch = (char)(value[left] & 0xff);
if (ch != ' ' && ch != 't' && !Character.isWhitespace(ch)) {
break;
}
left++;
}
return left;
}
public static int lastIndexOfNonWhitespace(byte[] value) {
int length = value.length;
int right = length;
while (0 < right) {
char ch = (char)(value[right - 1] & 0xff);
if (ch != ' ' && ch != 't' && !Character.isWhitespace(ch)) {
break;
}
right--;
}
return right;
}
Новый метод определяет вообще все случаи, когда символа не видно, будь то пробел, табуляция и проч. (желающие могут залезть в дебри реализации isWhiteSpace).
Таким образом, новый метод предпочтительнее, если Вы хотите отсечь не только пробелы, но и вообще все невидимые символы.
isBlank();
Метод возвращает результат запроса, содержит ли данная строка какие-то символы, кроме пробелов.
То есть, если мы исполним такой код:
String blank = " ";
Boolean isBlank = blank.isBlank();
Результат будет:
true
Внутри самого метода существует две реализации — для латинских символов и для строки в кодировке UTF-16.
public boolean isBlank() {
return indexOfNonWhitespace() == length();
}
private int indexOfNonWhitespace() {
if (isLatin1()) {
return StringLatin1.indexOfNonWhitespace(value);
} else {
return StringUTF16.indexOfNonWhitespace(value);
}
}
repeat();
Этот метод копирует содержимое строки заданное количество раз и возвращает результат в одной строке.
Например, выполнив код:
String sample = "(^_^) ";
String multiple = sample.repeat(10);
Мы получим:
(^_^) (^_^) (^_^) (^_^) (^_^) (^_^) (^_^) (^_^) (^_^) (^_^)
Если же количество итераций равно нулю, то строка не будет содержать символов вообще.
String blank = sample.repeat(0);
Результат:
length: 0
lines();
Странно было бы ожидать от Oracle, что они выпустят обновление String, не включив в класс какую-нибудь реализацию Stream API. И они-таки включили функционал в класс String.
Метод lines преобразует все строчки строки в соответствующий Stream. Выглядит это так:
String lines = "Blind Text Generator is a useful tooln" +
"which provides Lorem Ipsum and a number of alternatives.n" +
"The number of characters, words, and paragraphsn" +
"are easily controlled and you can set n" +
"the font to appreciate how it’ll look in your design.";
lines
.lines()
.map(l -> "next line: " + l)
.forEach(System.out::println);
Получим результат:
next line: Blind Text Generator is a useful tool
next line: which provides Lorem Ipsum and a number of alternatives.
next line: The number of characters, words, and paragraphs
next line: are easily controlled and you can set
next line: the font to appreciate how it’ll look in your design.
Мы получили полноценный стрим, с которым потом можем делать всё, что мы обычно делаем со стримами обычными. Применение этому может быть самое разное, и, хочется надеяться, что такая фича будет тепло принята разработчиками.
Если посмотреть внутрь самого метода, то мы увидим, что для преобразования строки в Stream используются два сплитератора на выбор, в зависимости от того, в какой кодировке строка.
public Stream<String> lines() {
return isLatin1() ? StringLatin1.lines(value)
: StringUTF16.lines(value);
}
На этом список нововведений релиза в части String заканчивается. Если я что-то упустил, буду рад об этом узнать и добавить в обзор. Все представленные примеры кода Вы можете пощупать самостоятельно в ресурсе на гитхабе.
Автор: Вячеслав Чернышов