2022-06-03 09:00:11
Как сравнить строки без учёта регистра
В этом посте расскажу, как решить задачу из предыдущего поста и сделать метод быстрее, чем в JDK.
Напомню задачу: сравнить две строки без учёта регистра и оценить производительность в разных ситуациях.
Шаг 1. Смотрим подходящие методы в классе String
s1.toLowerCase().equals(s2.toLowerCase())
s1.equalsIgnoreCase(s2)
s1.compareToIgnoreCase(s2) != 0
s1.regionMatches(true, 0, s2, 0, s2.length())
Шаг 2. Предположим возможные ситуации
Строки могут сильно и слабо отличаться по регистру.
Скорее всего, алгоритмы для пустых, коротких и длинных строк одни и те же, так что не будем различать эти случаи.
Шаг 3. Углубляемся в реализацию
toLowerCase + equals
Для каждой строки создаёт копию в нижнем регистре. Затем включается обычный equals:
Сравнивает длины строк. Если не равны, сразу возвращается false
Сравнивает по одному символу, пока не дойдёт до конца строки или пока символы не будут разными
В начале целиком обходим s1 и s2, чтобы создать копии в нижнем регистре. Когда длины строк отличаются (что можно узнать сразу), эта работа бесполезна
Если строки мало отличаются по регистру, то приведение всех символов к одному регистру будет лишним
compareToIgnoreCase cравнивает элементы по порядку. Если символы не равны — вызывает апперкейс и сравнивает ещё раз.
Uppercase происходит только при необходимости. Если разница в регистрах небольшая (s1=java, s2=Java), то этот подход будет быстрее
Цель метода — сравнить строки, поэтому нет быстрой проверки длины
regionMatches берёт символы из строк s1 и s2, сразу делает uppercase и сравнивает
Если строки по регистрам сильно отличаются, предварительный апперкейс ускорит проверку
Метод работает с подстроками произвольной длины, поэтому нет быстрой проверки длин
equalsIgnoreCase сравнивает длины строк, потом вызывает regionMatches
Итог
Если строки разной длины, то однозначно побеждает
equalsIgnoreCase
Если длины одинаковы и
— регистры не сильно отличаются, то побеждает
compareToIgnoreCase
— нужно много преобразований —
regionMatches equalsIgnoreCase для каждого символа делается апперкейс. Если строки по регистрам слабо отличаются, то это явно лишнее. Для таких случаев подойдёт кастомный метод:
if (s1.length() != s2.length()) {
return false;
}
return s1.compareToIgnoreCase(s2) != 0;
Лучшее из двух миров — быстрая проверка по длине и нет лишних апперкейсов.
Ниже — бенчмарки всех вариантов. Результаты на разных железках могут отличаться!
317 views06:00