1. Обзор
В этом уроке мы рассмотрим несколько способов проверки того, содержит ли строка подстроку, и сравним производительность каждого из них.
2. String.indexOf
Давайте сначала попробуем использовать метод String.indexOf . indexOf дает нам первую позицию, в которой найдена подстрока, или -1, если она вообще не найдена.
Когда мы будем искать “Rhap”, он вернет 9:
Assert.assertEquals(9, "Bohemian Rhapsodyan".indexOf("Rhap"));
Когда мы ищем “rhap”, он вернет -1, потому что он чувствителен к регистру.
Assert.assertEquals(-1, "Bohemian Rhapsodyan".indexOf("rhap")); Assert.assertEquals(9, "Bohemian Rhapsodyan".toLowerCase().indexOf("rhap"));
Также важно отметить, что если мы будем искать подстроку “an”, он вернет 6, потому что он возвращает первое вхождение:
Assert.assertEquals(6, "Bohemian Rhapsodyan".indexOf("an"));
3. Строка.содержит
Далее давайте попробуем String.contains . содержит будет искать подстроку по всей строке и вернет true , если она найдена, и false в противном случае.
В этом примере содержит возвращает true , потому что “Он” найден.
Assert.assertTrue("Hey Ho, let's go".contains("Hey"));
Если строка не найдена, содержит возвращает false :
Assert.assertFalse("Hey Ho, let's go".contains("jey"));
В последнем примере “hey” не найден, потому что String.contains чувствителен к регистру.
Assert.assertFalse("Hey Ho, let's go".contains("hey")); Assert.assertTrue("Hey Ho, let's go".toLowerCase().contains("hey"));
Интересным моментом является то, что содержит внутренние вызовы indexOf , чтобы узнать, содержится ли подстрока или нет.
4. StringUtils.containsIgnoreCase
Наш третий подход будет использовать StringUtils# containsIgnoreCase из библиотеки Apache Commons Lang |:
Assert.assertTrue(StringUtils.containsIgnoreCase("Runaway train", "train")); Assert.assertTrue(StringUtils.containsIgnoreCase("Runaway train", "Train"));
Мы видим , что он проверит, содержится ли подстрока в Строке , игнорируя регистр . Вот почему containsIgnoreCase возвращает true , когда мы ищем “Поезд”, а также “поезд” внутри “Поезда-беглеца”.
Этот подход не будет таким же эффективным, как предыдущие подходы , поскольку требуется дополнительное время, чтобы игнорировать случай. containsIgnoreCase внутренне преобразует каждую букву в верхний регистр и сравнивает преобразованные буквы вместо исходных.
5. Использование шаблона
Наш последний подход будет использовать Шаблон с регулярным выражением :
Pattern pattern = Pattern.compile("(?
Мы можем заметить, что сначала нам нужно построить Шаблон , затем нам нужно создать Совпадения , и, наконец, мы можем проверить с помощью метода find , есть ли вхождение подстроки или нет:
Matcher matcher = pattern.matcher("Hit the road Jack"); Assert.assertTrue(matcher.find());
Например, при первом выполнении find он возвращает true , потому что слово “дорога” содержится внутри строки “Hit the road Jack”, но когда мы пытаемся найти то же самое слово в строке “и больше не возвращайся”, оно возвращает false:
Matcher matcher = pattern.matcher("and don't you come back no more"); Assert.assertFalse(matcher.find());
6. Сравнение производительности
Мы будем использовать платформу микро-бенчмарка с открытым исходным кодом под названием Java Microbenchmark Harness (JMH), чтобы решить, какой метод является наиболее эффективным с точки зрения времени выполнения.
6.1. Настройка бенчмарка
Как и в каждом бенчмарке JMH, у нас есть возможность написать метод setup , чтобы иметь определенные вещи на месте до запуска наших бенчмарков:
@Setup public void setup() { message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in " + "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + "mollit anim id est laborum"; pattern = Pattern.compile("(?
В методе setup мы инициализируем поле message . Мы будем использовать это в качестве исходного текста для наших различных реализаций поиска.
Мы также инициализируем pattern , чтобы использовать его позже в одном из наших тестов.
6.2. Строка.Индекс бенчмарка
Наш первый бенчмарк будет использовать indexOf :
@Benchmark public int indexOf() { return message.indexOf("eiusmod"); }
Мы будем искать, в какой позиции “eiusmod” присутствует в переменной message .
6.3. Строка.содержит Бенчмарк
Наш второй тест будет использовать содержит :
@Benchmark public boolean contains() { return message.contains("eiusmod"); }
Мы попытаемся найти, содержит ли сообщение значение “eiusmod”, ту же подстроку , которая использовалась в предыдущем тесте.
6.4. Тест StringUtils.containsIgnoreCase
Наш третий тест будет использовать StringUtils# containsIgnoreCase :
@Benchmark public boolean containsStringUtilsIgnoreCase() { return StringUtils.containsIgnoreCase(message, "eiusmod"); }
Как и в предыдущих тестах, мы будем искать подстроку в сообщении значении.
6.5. Эталонный образец
И наш последний тест будет использовать Шаблон :
@Benchmark public boolean searchWithPattern() { return pattern.matcher(message).find(); }
Мы будем использовать шаблон, инициализированный в методе setup , для создания Matcher и сможем вызвать метод find , используя ту же подстроку, что и раньше.
6.6. Анализ результатов контрольных показателей
Важно отметить, что мы оцениваем результаты тестирования в наносекундах .
После завершения теста JMH мы можем увидеть среднее время, затраченное каждым из них:
- содержит : 14.736 нс
- indexOf : 14.200 нс
- содержит StringUtils IgnoreCase : 385.632 ns
- поиск По Шаблону : 1014.633 нс
Метод indexOf является наиболее эффективным, за ним следует метод contains . Имеет смысл, что содержит заняло больше времени, потому что использует indexOf внутренне.
содержит StringUtils IgnoreCase занял дополнительное время по сравнению с предыдущими, потому что он нечувствителен к регистру.
поиск с шаблоном занял еще более высокое среднее время , чем предыдущий, доказав, что использование Pattern s является худшей альтернативой для этой задачи.
7. Заключение
В этой статье мы рассмотрели различные способы поиска подстроки в строке . Мы также провели сравнительный анализ производительности различных решений.
Как всегда, код доступен на GitHub .