Как сравнить строки в Java?
Я использую оператор ==
в своей программе для сравнения строк, и до недавнего времени это работало исправно. Однако я столкнулся с ошибкой, и, изменив один из операторов на .equals()
, мне удалось её исправить.
В связи с этим у меня возникли вопросы: действительно ли использование ==
является плохой практикой? В каких случаях его следует использовать, а в каких — нет? В чем заключается разница между этими двумя способами сравнения?
5 ответ(ов)
В Java оператор ==
сравнивает ссылки на объекты, в то время как метод .equals()
сравнивает значения строк.
Иногда может показаться, что ==
сравнивает значения, потому что Java выполняет некоторые внутренние оптимизации, чтобы обеспечить идентичные строковые литералы, которые действительно являются одним и тем же объектом.
Пример:
String fooString1 = new String("foo");
String fooString2 = new String("foo");
// Возвращает false
fooString1 == fooString2;
// Возвращает true
fooString1.equals(fooString2);
// Возвращает true, так как Java использует один и тот же объект
"bar" == "bar";
Но остерегайтесь null!
Оператор ==
корректно обрабатывает строковые переменные, равные null
, но вызов метода .equals()
для строки, равной null
, приведет к исключению:
String nullString1 = null;
String nullString2 = null;
// Возвращает true
System.out.print(nullString1 == nullString2);
// Вызывает NullPointerException
System.out.print(nullString1.equals(nullString2));
Поэтому, если вы знаете, что fooString1
может быть null
, лучше сообщить об этом читателю, написав:
System.out.print(fooString1 != null && fooString1.equals("bar"));
Следующие варианты короче, но менее очевидно, что они проверяют на null
:
System.out.print("bar".equals(fooString1)); // "bar" никогда не равен null
System.out.print(Objects.equals(fooString1, "bar")); // требуется Java 7
==
сравнивает ссылки на объекты.
.equals()
сравнивает значения строк.
Иногда ==
создает иллюзию сравнения значений строк, как в следующих случаях:
String a = "Test";
String b = "Test";
if (a == b) // результат: true
Это происходит потому, что при создании любой строковой литералы JVM сначала ищет этот литерал в пуле строк, и если находит совпадение, то возвращает ту же ссылку на новую строку. В результате мы получаем:
(a == b) ⇒ true
Пул строк
b -----------------> "Test" <----------------- a
Однако ==
дает неверный результат в следующем случае:
String a = "test";
String b = new String("test");
if (a == b) // результат: false
В этом случае для выражения new String("test")
создается новый объект в куче, и эта ссылка присваивается переменной b
. Таким образом, b
будет ссылаться на строку в куче, а не в пуле строк.
Теперь a
указывает на строку в пуле строк, а b
указывает на строку в куче. Из-за этого мы получаем:
if (a == b) ⇒ false.
Пул строк
"test" <-------------------- a
Куча
"test" <-------------------- b
Тем временем, метод .equals()
всегда сравнивает значение строк, поэтому дает true
в обоих случаях:
String a = "Test";
String b = "Test";
if (a.equals(b)) // результат: true
String a = "test";
String b = new String("test");
if (a.equals(b)) // результат: true
Таким образом, использовать .equals()
всегда предпочтительнее.
Оператор ==
проверяет, являются ли два объекта строками именно одним и тем же объектом в памяти.
Метод .equals()
проверяет, имеют ли две строки одинаковое значение.
Строки в Java являются неизменяемыми. Это значит, что при попытке изменить или модифицировать строку создаётся новый экземпляр. Вы не можете изменить исходную строку. Это сделано для того, чтобы эти экземпляры строк могли кэшироваться. В типичной программе содержится множество ссылок на строки, и кэширование этих экземпляров может снизить потребление памяти и увеличить производительность программы.
Когда вы используете оператор ==
для сравнения строк, вы не сравниваете их содержимое, а фактически сравниваете адреса в памяти. Если оба адреса равны, вернётся true
, в противном случае — false
. В то время как метод equals
сравнивает именно содержимое строк.
Теперь возникает вопрос: если все строки кэшируются в системе, почему ==
может вернуть false
, а equals
— true
? Это возможно. Если вы создаёте новую строку, например, так: String str = new String("Testing")
, вы создаёте новый экземпляр строки в кэше, даже если в кэше уже существует строка с таким же содержимым. Проще говоря, выражение "MyString" == new String("MyString")
всегда вернёт false
.
Java также предлагает метод intern()
, который можно использовать для того, чтобы строка стала частью кэша, поэтому выражение "MyString" == new String("MyString").intern()
вернёт true
.
Важно отметить, что оператор ==
работает значительно быстрее, чем equals
, потому что вы сравниваете только адреса памяти. Однако вам нужно быть уверенным, что ваш код не создаёт новые экземпляры строк. В противном случае вы можете столкнуться с ошибками.
Когда вы используете оператор ==
для сравнения строк в Java, вы сравниваете не их содержимое, а ссылки на объекты. В вашем примере:
String a = new String("foo");
String b = new String("foo");
System.out.println(a == b); // выводит false
System.out.println(a.equals(b)); // выводит true
Здесь a
и b
указывают на разные объекты в памяти, даже если их содержимое одинаково. Поэтому выражение a == b
возвращает false
.
С другой стороны, метод equals()
сравнивает содержимое строк по символам. Он проверяет, равны ли строки по содержимому, и в этом случае a.equals(b)
возвращает true
, так как обе строки содержат "foo".
Таким образом, если хотите сравнивать строки по их содержимому в Java, всегда используйте метод equals()
, а не оператор ==
.
Как преобразовать строку в int в Java?
Как разделить строку в Java?
Почему сравнение строк с помощью '==' и 'is' иногда дает разные результаты?
Как сгенерировать случайную алфавитно-цифровую строку
Как преобразовать int в String?