Давай поговорим о методе String split: что он делает и зачем нужен. Несложно догадаться, что он делит строку, но как это работает на практике? Давай подробно рассмотрим работу метода и обсудим некоторые неочевидные детали, а заодно узнаем, сколько методов split есть в классе String на самом деле. Погнали!

Определение и сигнатура для Java String.split

Метод split в Java разделяет строку на подстроки, используя разделитель, который определяется с помощью регулярного выражения. Приведем сигнатуру метода и начнем наше погружение:

String[] split(String regex)
Из сигнатуры ясны две вещи:
  1. Метод возвращает массив строк.
  2. Метод принимает строку regex в качестве параметра.
Разберем каждую вещь по отдельности в разрезе определения, приведенного выше.
  1. Метод возвращает массив строк.

    В определении есть такие слова: "Метод split в Java разделяет строку на подстроки". Данные подстроки собираются методом в массив и представляют собой его возвращаемое значение.

  2. Метод принимает строку regex в качестве параметра.

    Опять же, вспомним определение: "разделяет строку на подстроки, используя разделитель, который определяется с помощью регулярного выражения". Принимаемый параметр regex — это шаблон регулярного выражения, который применяется к исходной строке и по совпадениям определяет в ней символ (или комбинацию символов) разделителя.

Метод split в Java: делим строку на части - 1

Split на практике

Теперь ближе к делу. Представим, что у нас есть строка со словами. Например, такая:
I love Java
Нам нужно разбить строку на слова. Мы видим, что в данной строке слова разделены друг от друга пробелами. Пробел — идеальный кандидат на роль разделителя в данном случае. Так выглядит код решения данной задачи:

public class Main {
    public static void main(String[] args) {
        String str = "I love Java";
        String[] words = str.split(" ");
        for (String word : words) {
            System.out.println(word);
        }
    }
}
Выводом метода main будут следующие строки:
I love Java
Посмотрим еще на несколько примеров того, как бы отработал метод split:
Строка Разделитель Результат работы метода
"I love Java" " " (символ пробела) {"I", "love", "Java"}
"192.168.0.1:8080" ":" {"192.168.0.1", "8080"}
"Красный, оранжевый, желтый" "," {"Красный", " оранжевый", " желтый"}
"Красный, оранжевый, желтый" ", " {"Красный", "оранжевый", "желтый"}
Обрати внимание на различия между последними двумя строками в таблице выше. В предпоследней строке разделителем выступает символ запятой, поэтому строка разбилась таким образом, что в некоторых словах есть ведущие пробелы. В последней же строке в качестве разделителя мы использовали символ запятой и символ пробела. Поэтому в результирующем массиве не оказалось строк с ведущими пробелами. Это просто маленькая деталь, в которой демонстрируется, как важно внимательно подбирать правильный разделитель.

Ведущий разделитель

Есть еще один важный нюанс. Если исходная строка начинается с разделителя, первым элементом результирующего массива будет пустая строка. На примере это будет выглядеть так: Исходная строка: " I love Java" Разделитель: " " Результирующий массив: { "", "I", "love", "Java" } Но если исходная строка заканчивается разделителем, а не начинается, результат будет иным: Исходная строка: "I love Java " Разделитель: " " Результирующий массив: { "I", "love", "Java" } Смотрим в коде на вариации работы метода split с символом разделителя в конце и/или начале исходной строки:

public class Main {
    public static void main(String[] args) {
        print("I love Java".split(" "));
        print(" I love Java".split(" "));
        print("I love Java ".split(" "));
        print(" I love Java ".split(" "));
    }

    static void print(String[] arr) {
        System.out.println(Arrays.toString(arr));
    }
}
Вывод метода main будет таким:
[I, love, Java] [, I, love, Java] [I, love, Java] [, I, love, Java]
Еще раз обрати внимание: когда в исходной строке первый символ — это символ разделителя, в результате в массиве первым элементом будет пустая строка.

Перегруженный собрат

В классе String есть еще один метод split с такой сигнатурой:

String[] split(String regex, int limit)
У этого метода есть дополнительный параметр limit: он определяет, какое количество раз шаблон regex будет применяться к исходной строке. Ниже — пояснения:

limit > 0

Применяется limit-1 раз. При этом длина массива не будет превышать значение limit. Последним элементом массива будет часть строки, следующая за последним найденным разделителем. Пример:

public class Main {
    public static void main(String[] args) {
        print("I love Java".split(" ", 1));
        print("I love Java".split(" ", 2));
        /*
         Вывод: 
         [I love Java]
         [I, love Java]
        */
    }

    static void print(String[] arr) {
        System.out.println(Arrays.toString(arr));
    }
}

limit < 0

Шаблон для поиска разделителя применяется к строке столько раз, сколько это возможно. Длина итогового массива может быть любой. Пример:

public class Main {
    public static void main(String[] args) {
        // Обратите внимание на пробел в конце строки
        print("I love Java ".split(" ", -1));
        print("I love Java ".split(" ", -2));
        print("I love Java ".split(" ", -12));
        /*
         Вывод:
        [I, love, Java, ]
        [I, love, Java, ]
        [I, love, Java, ]
        
        Обратите внимание: последний элемент массива —
        пустая строка, возникшая из-за пробела
        в конце исходной строки. 
        */
    }

    static void print(String[] arr) {
        System.out.println(Arrays.toString(arr));
    }
}

limit 0

Как и в случае с limit < 0, шаблон для поиска разделителя применяется к строке столько раз, сколько это возможно. Итоговый массив может быть любой длины. Если последние элементы равны пустой строке, в итоговом массиве они будут отброшены. Пример:

public class Main {
    public static void main(String[] args) {
        // Обратите внимание на пробел в конце строки
        print("I love Java ".split(" ", 0));
        print("I love Java ".split(" ", 0));
        print("I love Java ".split(" ", 0));
        /*
         Вывод:
        [I, love, Java]
        [I, love, Java]
        [I, love, Java]
        Обратите внимание на отсутствие пустых строк в конце                       массивов
        */
    }

    static void print(String[] arr) {
        System.out.println(Arrays.toString(arr));
    }
}
Если мы посмотрим на реализацию метода split с одним аргументом, увидим, что данный метод вызывает своего перегруженного собрата со вторым аргументом, равным нулю:

    public String[] split(String regex) {
        return split(regex, 0);
    }

Различные примеры

В рабочей практике иногда бывает так, что у нас есть строка, составленная по определенным правилам. Данная строка может "приходить" в нашу программу откуда угодно:
  • из стороннего сервиса;
  • из запроса к нашему серверу;
  • из конфигурационного файла;
  • и т.д.
Обычно в такой ситуации программисту известны "правила игры". Скажем, программист знает, что у него есть информация о пользователе, которая хранится по такому шаблону:
user_id|user_login|user_email
Для примера возьмем конкретные значения:
135|bender|bender@gmail.com
И вот перед программистом стоит задача: написать метод, который отправляет электронное письмо пользователю. В его распоряжении — информация о юзере, записанная в указанном выше формате. Ну а подзадача, которую мы продолжим разбирать — вычленить email-адрес из общей информации о пользователе. Это один из примеров, когда метод split может оказаться полезным. Ведь если мы взглянем на шаблон, мы поймем: чтобы вычленить email-адрес пользователя из всей информации, нам нужно всего лишь разделить строку с помощью метода split. Тогда email-адрес будет лежать в последнем элементе результирующего массива. Приведем пример такого метода, который принимает в себя строку, содержащую информацию о пользователе, и возвращает email пользователя. Для упрощения предположим, что данная строка всегда соответствует нужному нам формату:

public class Main {
    public static void main(String[] args) {
        String userInfo = "135|bender|bender@gmail.com";
        System.out.println(getUserEmail(userInfo));
        // Вывод: bender@gmail.com
    }

    static String getUserEmail(String userInfo) {
        String[] data = userInfo.split("\\|");
        return data[2]; // или data[data.length - 1]
    }
}
Обратите внимание на разделитель: "\\|". Так как в регулярных выражениях “|” — это специальный символ, на котором завязана определенная логика, чтобы использовать его как обычный (тот, который мы хотим найти в исходной строке), нужно экранировать этот символ с помощью двух обратных слешей. Рассмотрим другой пример. Допустим, у нас есть информация о заказе, которая записана примерно в таком формате:
item_number_1,item_name_1,item_price_1;item_number_2,item_name_2,item_price_2;...;item_number_n,item_name_n,item_price_n
Ну или же возьмем конкретные значения:
1,огурцы,20.05;2,помидоры,123.45;3,зайцы,0.50
Перед нами стоит задача: рассчитать итоговую стоимость заказа. Здесь нам придется применять метод split несколько раз. Первым шагом мы разделим строку через символ ";", на составные части. Тогда в каждой такой части у нас будет информация об отдельном товаре, которую мы сможем обработать в дальнейшем. А затем, в рамках каждого товара будем разделять информацию с помощью символа "," и брать из результирующего массива элемент с определенным индексом (в котором хранится цена), приводить ее к числовому виду и составлять итоговую стоимость заказа. Напишем метод, который все это посчитает:

public class Main {
    public static void main(String[] args) {
        String orderInfo = "1,огурцы,20.05;2,помидоры,123.45;3,зайцы,0.50";
        System.out.println(getTotalOrderAmount(orderInfo));
        // Вывод: 144.0
    }

    static double getTotalOrderAmount(String orderInfo) {
        double totalAmount = 0d;
        final String[] items = orderInfo.split(";");

        for (String item : items) {
            final String[] itemInfo = item.split(",");
            totalAmount += Double.parseDouble(itemInfo[2]);
        }

        return totalAmount;
    }
}
Попробуй самостоятельно разобраться с тем, как работает данный метод. На основе данных примеров можно сказать, что метод split используется тогда, когда у нас есть некоторая информация в строковом виде, из которой нам необходимо вычленить какую-то более специфическую информацию.

Итоги

Мы рассмотрели с тобой метод split класса String. Он нужен для разделения строки на составные части с помощью специального разделителя. Метод возвращает массив строк (составные части строки). Принимает в себя регулярное выражение, по которому находит символ(ы) разделителя. Мы рассмотрели различные тонкости работы данного метода:
  • ведущий символ разделителя;
  • перегруженный собрат с двумя аргументами.
Также попробовали смоделировать некоторые ситуации “из жизни”, в которых использовали метод split для решения пусть и вымышленных, но вполне реалистичных проблем.