«1. Введение

Строковые значения и операции довольно часто встречаются в повседневной разработке, и любой Java-разработчик должен уметь с ними обращаться.

В этом уроке мы предоставим краткую памятку по общим операциям со строками.

Кроме того, мы прольем свет на различия между equals и «==» и между StringUtils#isBlank и #isEmpty.

2. Преобразование Char в строку

Char представляет один символ в Java. Но в большинстве случаев нам нужна строка.

Итак, давайте начнем с преобразования символов в строки:

String toStringWithConcatenation(final char c) {
    return String.valueOf(c);
}

3. Добавление строк

Другой часто необходимой операцией является добавление строк с другими значениями, такими как char:

String appendWithConcatenation(final String prefix, final char c) {
    return prefix + c;
}

Мы можем добавить другие базовые типы также с помощью StringBuilder:

String appendWithStringBuilder(final String prefix, final char c) {
    return new StringBuilder(prefix).append(c).toString();
}

4. Получение символа по индексу

Если нам нужно извлечь один символ из строки, API предоставляет все, что нам нужно:

char getCharacterByIndex(final String text, final int index) {
    return text.charAt(index);
}

Поскольку String использует char[] в качестве резервной структуры данных, индекс начинается с нуля.

5. Обработка значений ASCII

Мы можем легко переключаться между char и его числовым представлением (ASCII) с помощью приведения:

int asciiValue(final char character) {
    return (int) character;
}

char fromAsciiValue(final int value) {
    Assert.isTrue(value >= 0 && value < 65536, "value is not a valid character");
    return (char) value;
}

Конечно, поскольку int состоит из 4 байтов без знака, а char — из 2 байт, нам нужно проверить, чтобы убедиться, что мы работаем с допустимыми символьными значениями.

6. Удаление всех пробелов

Иногда нам нужно избавиться от некоторых символов, чаще всего от пробелов. Хороший способ — использовать метод replaceAll с регулярным выражением:

String removeWhiteSpace(final String text) {
    return text.replaceAll("\\s+", "");
}

7. Присоединение коллекций к строке

Еще один распространенный вариант использования — когда у нас есть какая-то коллекция и мы хотим создать строку из it:

<T> String fromCollection(final Collection<T> collection) { 
   return collection.stream().map(Objects::toString).collect(Collectors.joining(", "));
}

Обратите внимание, что Collectors.joining позволяет указывать префикс или суффикс.

8. Разделение строки

Или, с другой стороны, мы можем разделить строку по разделителю, используя метод split:

String[] splitByRegExPipe(final String text) {
   return text.split("\\|");
}

Опять же, здесь мы используем регулярное выражение, на этот раз для разделен трубой. Поскольку мы хотим использовать специальный символ, мы должны его экранировать.

Другой возможностью является использование класса Pattern:

String[] splitByPatternPipe(final String text) {
    return text.split(Pattern.quote("|"));
}

9. Обработка всех символов как потока

В случае детальной обработки мы можем преобразовать строку в IntStream:

IntStream getStream(final String text) {
    return text.chars();
}

~~ ~ 10. Равенство ссылок и равенство значений

Хотя строки выглядят как примитивный тип, это не так.

Следовательно, мы должны различать равенство ссылок и равенство значений. Равенство ссылок всегда подразумевает равенство значений, но, как правило, не наоборот. Первое мы проверяем с помощью операции «==», а второе — с помощью метода equals:

@Test
public void whenUsingEquals_thenWeCheckForTheSameValue() {
    assertTrue("Values are equal", new String("Test").equals("Test"));
}

@Test
public void whenUsingEqualsSign_thenWeCheckForReferenceEquality() {
    assertFalse("References are not equal", new String("Test") == "Test");
}

Обратите внимание, что литералы интернированы в пуле строк. Поэтому компилятор может время от времени оптимизировать их для одной и той же ссылки:

@Test
public void whenTheCompileCanBuildUpAString_thenWeGetTheSameReference() {
    assertTrue("Literals are concatenated by the compiler", "Test" == "Te"+"st");
}

11. Пустая строка и пустая строка

Между isBlank и isEmpty есть тонкая разница.

Строка считается пустой, если она нулевая или имеет нулевую длину. Принимая во внимание, что строка является пустой, если она нулевая или содержит только пробельные символы:

@Test
public void whenUsingIsEmpty_thenWeCheckForNullorLengthZero() {
    assertTrue("null is empty", isEmpty(null));
    assertTrue("nothing is empty", isEmpty(""));
    assertFalse("whitespace is not empty", isEmpty(" "));
    assertFalse("whitespace is not empty", isEmpty("\n"));
    assertFalse("whitespace is not empty", isEmpty("\t"));
    assertFalse("text is not empty", isEmpty("Anything!"));
}

@Test
public void whenUsingIsBlank_thenWeCheckForNullorOnlyContainingWhitespace() {
    assertTrue("null is blank", isBlank(null));
    assertTrue("nothing is blank", isBlank(""));
    assertTrue("whitespace is blank", isBlank("\t\t \t\n\r"));
    assertFalse("test is not blank", isBlank("Anything!"));
}

12. Заключение

Строки являются основным типом во всех видах приложений. В этом руководстве мы изучили некоторые ключевые операции в распространенных сценариях.

Кроме того, мы дали указания по более подробным ссылкам.

Наконец, полный код со всеми примерами доступен в нашем репозитории GitHub.