W3docs

Как сравнивать строки в Java

Сравнение строк Java с помощью equals, equalsIgnoreCase, compareTo и частые ошибки с ==.

Сравнение двух значений String в Java — одна из первых вещей, которую начинающий делает неправильно, потому что оператор == тихо сравнивает ссылки, а не содержимое. В этой главе рассматриваются правильные, идиоматические способы проверки строк на равенство и упорядочение — equals, equalsIgnoreCase, compareTo и null-безопасный Objects.equals — и объясняется, когда каждый из них является подходящим инструментом.

Об основной причине, по которой одинаковые литералы могут быть ==, см. пул строк; более широкий обзор методов сравнения — в статье сравнение строк Java.

Равенство содержимого с помощью equals

Чтобы проверить, содержат ли две строки одинаковые символы, вызовите equals:

String a = "hello";
String b = new String("hello");

boolean same = a.equals(b);   // true — same characters

equals обходит две последовательности символов и возвращает true только тогда, когда они имеют одинаковую длину и одинаковые символы в одинаковом порядке. Это метод, который нужен вам в 99% случаев, когда вы спрашиваете «содержат ли эти две строки одинаковый текст?».

Распространённый защитный приём — вызывать equals на строковом литерале, чтобы получатель никогда не был null:

if ("yes".equals(userInput)) { ... }   // safe even if userInput is null

Почему == — классическая ловушка

== вообще не смотрит на символы — он спрашивает «являются ли эти объекты одним и тем же объектом в памяти?». Поскольку компилятор интернирует строковые литералы (хранит одну общую копию в пуле), два одинаковых литерала могут оказаться ==, что вводит людей в заблуждение, будто == сравнивает текст. Это не так: как только строка получена через new, из пользовательского ввода или конкатенации во время выполнения, == возвращает false даже для идентичного текста.

ВыражениеРезультатПочему
"java" == "java"trueОба ссылаются на один и тот же интернированный литерал
"java" == new String("java")falsenew создаёт отдельный объект
"java".equals(new String("java"))trueСравнивает содержимое, а не идентичность

Практическое правило: никогда не используйте == для сравнения строк. Используйте equals.

Сравнение без учёта регистра с equalsIgnoreCase

Когда регистр не важен — имена пользователей, расширения файлов, имена заголовков — используйте equalsIgnoreCase:

"Java".equalsIgnoreCase("JAVA");   // true

Метод применяет ту же посимвольную логику, что и equals, после приведения регистра, поэтому "README".equalsIgnoreCase("readme") возвращает true.

Упорядочение с помощью compareTo

Чтобы сортировать или ранжировать строки, а не просто проверять равенство, используйте compareTo, который возвращает знак лексикографической (словарной) разности:

"apple".compareTo("banana");   // negative — apple comes first
"apple".compareTo("apple");    // 0       — equal
"banana".compareTo("apple");   // positive — banana comes later

Отрицательный результат означает, что получатель сортируется перед аргументом, 0 означает равенство, положительный — после. Существует вариант compareToIgnoreCase, а также String.CASE_INSENSITIVE_ORDER для использования в качестве Comparator. Обратите внимание, что сортировка по умолчанию производится по кодовым позициям Unicode, поэтому все заглавные буквы (AZ) сортируются перед строчными.

Наглядный пример

Эта программа демонстрирует все подходы рядом, чтобы вы могли точно увидеть поведение каждого из них.

java— editable, runs on the server

Что следует взять из этого запуска:

  • a == b выводит false, хотя обе строки содержат «java», потому что new String("java") — это отдельный объект — доказательство того, что == сравнивает идентичность, а не текст.
  • a.equals(b) выводит true: equals смотрит на символы, что почти всегда и подразумевается под «одинаковой строкой».
  • a == c выводит true только потому, что оба являются литералами, разделяющими один интернированный объект — именно эта случайная удача заманивает новичков в ошибку с ==.
  • equalsIgnoreCase выводит true для "Java" и "JAVA", а compareTo возвращает 0 для одинакового текста и ненулевой знак (-1 для apple перед banana), когда они отличаются.
  • Objects.equals(null, "x") выводит false вместо исключения, а отсортированный массив выводится как [Cherry, apple, banana] — заглавная C (кодовая позиция 67) сортируется перед строчными буквами (кодовая позиция 97+).

Какой метод использовать?

Выбирайте метод сравнения в зависимости от вопроса, на который вы на самом деле отвечаете:

Вы хотите знать…ИспользуйтеПримечания
Является ли текст точно равным?a.equals(b)Выбор по умолчанию для равенства
Равны ли строки без учёта регистра?a.equalsIgnoreCase(b)Сначала приводит регистр
Равны ли, но одна из сторон может быть null?Objects.equals(a, b)Возвращает true для двух null, никогда не выбрасывает исключение
Каков их порядок сортировки?a.compareTo(b)Отрицательный / 0 / положительный
Порядок сортировки без учёта регистра?a.compareToIgnoreCase(b)Или String.CASE_INSENSITIVE_ORDER как Comparator

Полезная привычка: когда нужна только проверка равенства, предпочитайте equals (или Objects.equals, когда возможен null) и никогда не используйте compareTo(...) == 0, который выполняет строго больше работы. Чтобы изучить методы, на которых это основано, см. методы строк Java и класс String.

Практика

Практика
Почему выражение new String('hi') == 'hi' возвращает false в Java?
Почему выражение new String('hi') == 'hi' возвращает false в Java?
Was this page helpful?