64

В своей программе я использовал оператор == для сравнения строк. Но я наткнулся на баг, и при замене == на equals он пропал.​​​​​​​​​​

Следует избегать оператора ==? Когда его можно использовать, а когда нет? В чём разница?

Kyubey
  • 32,103
  • 5
    Добавил ещё один "канонический" ответ. На английском SO это самый популярный вопрос как цель для закрытия дубликатов. Преимущественно перевод и компиляция How do I compare strings in Java?, плюс ещё по мелочи. – Kyubey Apr 17 '15 at 15:07
  • 1
    Школа на Хэшкоде :) – VladD Apr 17 '15 at 15:08
  • Извини, но ты сам себя спросил, и сам же себе ответил? – Andrew Bystrov Apr 17 '15 at 15:29
  • 4
    @AndrewBystrov Да. См. http://ru.stackoverflow.com/help/self-answer – Kyubey Apr 17 '15 at 15:30
  • 1
    @AndrewBystrov на SO это нормально, хотя я думал что "самоответ" синхронно с вопросом это не комильфо, но в правилах противопоказаний нет – tutankhamun Apr 17 '15 at 18:49
  • Возможный дубликат вопроса: Как работает оператор == – ReinRaus Feb 22 '16 at 17:51
  • 1
    @ReinRaus Это самодостаточный QA с более скучкованным объяснением для закрытия как дубликатов, а не статья. Он оптимизирован для новичков и прекрасно работает. Не надо объединять 10 вопросов в один с менее внятными объяснениями. – Kyubey Feb 23 '16 at 01:04
  • 2
    @Discord хз. Может я и не прав, но лучше сделать один "эталонный" ответ и вопрос и закрывать на него все остальные точно такие же вопросы. Если возникает желание добавить по теме, то улучшать "эталонный" ответ. – ReinRaus Feb 23 '16 at 13:27
  • @ReinRaus "Эталонный" ответ по этой теме слишком обширен. Нельзя впихать все нюансы использования оператора равенства для всех типов в один ответ. Написанный недавно ответ — яркий тому пример. См. мои комментарии под ним. – Kyubey Feb 23 '16 at 15:32
  • ассоциация: http://stackoverflow.com/questions/513832/ – Nofate Feb 27 '17 at 17:45

2 Answers2

72

Оператор == сравнивает ссылки.

Метод equals сравнивает значения.

Следовательно, если вы хотите сравнить строки на равенство, следует использовать equals.

Однако в некоторых случаях строки гарантированно представлены одним и тем же объектом благодаря пулу строк (string interning). Эти случаи явно описаны в спецификации языка Java.

Оператор == используется для проверки, что две строки указывают на один и тот же объект.

// Эти строки имеют одно и тоже же значение
new String("test").equals("test") // --> true 

// ...но это разные объекты
new String("test") == "test" // --> false 

// ...эти строки тоже разные объекты
new String("test") == new String("test") // --> false 

// ...но эти строки указывают на один и тот же объект,
// потому что компилятор добавляет все литералы в пул.
"test" == "test" // --> true 

// Конкатенация литералов тоже происходит на стадии компиляции,
// поэтому они указывают на один объект
"test" == "te" + "st" // --> true

// но вызов substring() происходит во время выполнения,
// в результате получаются разные объекты.
"test" == "!test".substring(1) // --> false

// Строки из пула могут быть получены с помощью вызова intern().
"test" == "!test".substring(1).intern() // --> true

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

Остерегайтесь вызова equals на null! Оператор == прекрасно сравнивает строки, если одна или более из них равна null, но вызов метода equals на строке, равной null, приведёт к исключению.

Для сравнения строк, которые могут быть равны null, вы можете воспользоваться следующим методом:

public static boolean equals(String str1, String str2) {
    return str1 == null ? str2 == null : str1.equals(str2);
}

Он присутствует в некоторых сторонних библиотеках, например, в Apache Commons.

Если вы пользуетесь современными средами разработки, то они предупредят, если вы попытаетесь сравнить строки с помощью оператора ==. Всегда обращайте внимание на подобные предупреждения.

Kyubey
  • 32,103
  • 2
    Если это канонический ответ, может, расписать про interning? – VladD Apr 17 '15 at 15:09
  • @VladD По-моему, это отдельный вопрос. Для тех, кто интересуется, как сравнивать строки, детали работы с interning несколько рано рассказывать. :) – Kyubey Apr 17 '15 at 15:14
  • Может тогда вместо "Однако в некоторых случаях строки гарантированно представлены одним и тем же объектом" например "В некоторых случаях сравнение через == может давать правильный результат (см. interning насчёт деталей), но надеяться на это нельзя, разве что вы отчётливо понимаете, что делаете."? – VladD Apr 17 '15 at 15:23
  • @VladD "Надеяться" — неудачное слово, когда есть формальная спецификация. Важные случаи описаны в примере, а тащить все детали из спеки в этот ответ было бы излишним. Если хотите написать про interning, можете создать отдельный QA, расписать критерии во всех подробностях, расписать случаи использования, достоинства, недостатки, подводные камни и т.п. Тогда из этого вопроса можно было бы сослаться. По-моему, это было бы оптимальным. – Kyubey Apr 17 '15 at 15:29
  • Ну мы же пишем неформальное объяснение? "Рассчитывать"? – VladD Apr 17 '15 at 15:33
  • Хм. Попробую написать про interning. – VladD Apr 17 '15 at 15:40
  • @VladD Я не хочу портить корректную формулировку: спека всё гарантирует, и загвоздка может быть только с неполным пониманием спеки. Можно добавить что-то в духе: "Если вы сомневаетесь и не знаете, каким методом воспользоваться, используйте equals. Не стоит просто полагаться на то, что в каком-то отдельном случае оператор == сработал." – Kyubey Apr 17 '15 at 15:40
  • Ну, или с тем, что документацию никто не читает :-\ – VladD Apr 17 '15 at 15:41
  • Написал: http://ru.stackoverflow.com/q/417843/10105 – VladD Apr 19 '15 at 19:49
  • Оператор == сравнивает ссылки. а как же быть с примитивными типами? – rjhdby May 24 '17 at 11:48
  • @rjhdby Применение оператора == и метода equals указано применительно к классу String. Вообще этот оператор сравнивает значения, просто значением переменной типа "строка" является указатель на адрес в памяти, где располагаются данные. Для примитивных типов значением является собственно значение. – Kyubey May 24 '17 at 12:08
  • @Squidward я в курсе. Просто на этот вопрос была дана ссылка с другого вопроса (т.е. он используется в качестве этакой wiki) и хорошо бы объяснить разницу без привязки конкретно к String, потому как человека не знающего фраза Метод equals сравнивает значения. может надолго ввести в заблуждение с последующими вопросами типа "а почему не работает int a=0;a.equals(0);?". Ну да на ваше усмотрение. – rjhdby May 24 '17 at 12:18
  • @rjhdby Чтобы объяснить работу оператора равенства в общем случае, нужно объяснить разницу между value-типами и reference-типами, а там уже вытягивается организация памяти и прочее. Я не представляю, как сформулировать объяснение и доступно, и полноценно, и практично одновременно. Поэтому этот QA я рассматриваю как "инструкцию по сравнению строк для новичков" и подробности описываемых методов считаю выходящими за его рамки. Подробности описаны в других QA, например https://ru.stackoverflow.com/q/496080, https://ru.stackoverflow.com/q/417843 и др. – Kyubey May 24 '17 at 16:58
6

Если быть коротко, то == сравнивает ссылки на объект, если ссылки указывают на один и тот же объект, то это тру, иначе false, в случае с примитивными типами == сравнивает значения.

equals () используется в String так, он берет и сравнивает посимвольно каждый String, но это только со String, если брать остальные объекты (Вы создали объекты Яблоко и Груша), при этом в этих классах не прописан метод equals,то оно как и == сравнивает ссылки на объект, если это один и тот же объект, то тру иначе false.

В String прописан метод equals (), который сравнивает посимвольно, поэтому со String нужно юзать equals ()