Avatar ·

Как сравнить строки без учета регистра в Kotlin? Аналог Java equalsIgnoreCase

Всем привет! Я только начал изучать Kotlin после Java и столкнулся с простой, но раздражающей проблемой. В Java я привык использовать метод `equalsIgnoreCase()` для сравнения строк. Например, я пишу приложение для Android, где нужно проверять введённый пользователем город. Пользователь может написать "Москва", "мосКВА" или "МОСКВА" — все эти варианты должны считаться одинаковыми. В Java это выглядело так: ```java String userInput = "мосКВА"; String city = "Москва"; if (city.equalsIgnoreCase(userInput)) { // Город совпал } ``` Но в Kotlin я не могу найти этот метод! Когда я пытаюсь написать `city.equalsIgnoreCase(userInput)`, компилятор выдаёт ошибку: "Unresolved reference: equalsIgnoreCase". Я пробовал использовать `==` для сравнения, но он чувствителен к регистру. Я уже гуглил, но ответы разрозненные. Кто-то пишет про `toLowerCase()`, кто-то про кастомные extension-функции. Мне нужно простое и надёжное решение для сравнения строк без учёта регистра, которое будет работать с русскими буквами и спецсимволами. Подскажите, пожалуйста, как правильно делать такое сравнение в Kotlin? Есть ли встроенный метод или идиоматический способ? Очень хочется писать код в стиле Kotlin, а не переносить Java-привычки. <<>>

Правильный способ: используйте equals(other, ignoreCase = true)

В Kotlin нет прямого метода equalsIgnoreCase(), как в Java, но есть гораздо более гибкий и идиоматичный подход. Класс String в Kotlin содержит метод equals() с дополнительным параметром ignoreCase.

val city = "Москва"
val userInput = "мосКВА"

if (city.equals(userInput, ignoreCase = true)) {
    println("Город совпал!") // Этот код выполнится
} else {
    println("Города разные")
}

Как это работает

Метод equals(other: Any?, ignoreCase: Boolean = false): Boolean — это встроенная функция Kotlin. Когда вы передаёте ignoreCase = true, сравнение выполняется без учёта регистра. По умолчанию ignoreCase = false, что эквивалентно обычному ==.

Важные нюансы

  • Null-безопасность: Этот метод защищает от NullPointerException. Если city или userInput равно null, метод вернёт false (кроме случая, когда оба null — тогда вернётся true).
  • Локализация: Метод корректно работает с символами Unicode, включая кириллицу, немецкие умлауты, французские акценты и т.д.
  • Производительность: В отличие от подхода toLowerCase(), этот метод не создаёт новые строки, что экономит память.

Сравнение с Java-подходом

Для наглядности приведу таблицу соответствия:

// Java
str1.equalsIgnoreCase(str2)

// Kotlin (эквивалент)
str1.equals(str2, ignoreCase = true)

// Или с использованием infix-нотации (но только для Boolean):
// В Kotlin нет infix для equals с ignoreCase, но можно сделать extension

Дополнительные примеры

Вот несколько сценариев использования:

// Проверка ввода пользователя
fun isValidPassword(input: String): Boolean {
    return "admin123".equals(input, ignoreCase = true)
}

// Сортировка списка без учёта регистра
val names = listOf("Анна", "борис", "Виктор", "антон")
val sorted = names.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it })
println(sorted) // [Анна, антон, борис, Виктор]

// Фильтрация
val cities = listOf("Москва", "Париж", "Лондон")
val search = "мос"
val filtered = cities.filter { it.startsWith(search, ignoreCase = true) }
println(filtered) // [Москва]

Когда использовать toLowerCase()?

Иногда можно встретить совет сравнивать через toLowerCase(). Избегайте этого, если нет веской причины:

// ПЛОХОЙ стиль — создаёт лишние объекты
if (city.toLowerCase() == userInput.toLowerCase()) { ... }

// ХОРОШИЙ стиль — без лишних аллокаций
if (city.equals(userInput, ignoreCase = true)) { ... }

Также помните, что toLowerCase() может давать разные результаты в зависимости от локали (например, в турецком языке буква "I" преобразуется по-особому). Метод с ignoreCase = true этого недостатка лишён.

Вывод

Для сравнения строк без учёта регистра в Kotlin всегда используйте equals(other, ignoreCase = true). Это идиоматично, безопасно и эффективно. Забудьте про Java-метод equalsIgnoreCase() — в Kotlin есть более элегантное решение.

<<>>

Альтернативный подход: Extension-функция для удобства

Если вы часто сравниваете строки без учёта регистра и хотите получить синтаксис, близкий к Java, можно написать свою extension-функцию:

fun String.equalsIgnoreCase(other: String): Boolean {
    return this.equals(other, ignoreCase = true)
}

// Использование
val city = "Москва"
val userInput = "мосКВА"
if (city.equalsIgnoreCase(userInput)) {
    println("Равны")
}

Теперь у вас есть метод, который выглядит и ведёт себя как Java-версия, но использует Kotlin-механизмы под капотом.

Другой вариант: оператор == с приведением

Можно также переопределить поведение, но это не рекомендуется:

// Не делайте так — это нарушает ожидания
operator fun String?.compareTo(other: String?): Int {
    return (this ?: "").compareTo(other ?: "", ignoreCase = true)
}

Для коллекций: contentEquals

Если сравниваете массивы символов или списки строк:

val arr1 = charArrayOf('A', 'B')
val arr2 = charArrayOf('a', 'b')
println(arr1.contentEquals(arr2, ignoreCase = true)) // true

Выберите тот способ, который лучше вписывается в ваш стиль кода. Но помните, что встроенный equals() с параметром — это самый идиоматичный и производительный вариант.