Рубрики
Без рубрики

Проверьте, содержит ли строка подстроку

Изучите различные способы поиска подстроки в строке с помощью тестов производительности

Автор оригинала: baeldung.

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 .