Вопрос по equality, comparison, java, equals – Когда «» == s ложно, но «» .equals (s) верно

32

EDIT Спасибо за быстрые ответы. Пожалуйста, посмотрите, каков реальный вопрос. Я сделал это смелым на этот раз.

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


Я выполняю проверку ниже для пустых строк:

if( "" == value ) { 
    // is empty string 
} 

вpast при получении значений из базы данных или десериализации объектов из другого узла этот тестfailedпотому что два экземпляра строки действительно были разными ссылками на объекты, хотя они содержали одни и те же данные.

Таким образом, исправление для этих ситуаций было

if( "".equals( value ) ) {
   // which returns true for all the empty strings
}

Я в порядке с этим. Это ясно понято.

Сегодня это произошло еще раз, но меня это озадачило, потому что на этот раз приложение очень маленькоеstandalone application который не используетnetwork at allпоэтому новая строка не извлекается из базы данных и не десериализуется с другого узла.

Итак, вопрос:


Under which OTHER circumstances:

"" == value // yields false 

а также

"".equals( value ) // yields true

Для локального автономного приложения?

Я уверенnew String() не используется в коде.

И единственный способ, которым ссылка на строку может быть & quot; & quot; потому что ему назначается & quot; & quot; непосредственно в коде (или как я думал) как в:

String a = "";
String b = a;

assert "" == b ; // this is true 

Каким-то образом (после прочтения кода у меня есть подсказка) были созданы две разные ссылки на пустые строковые объекты, я хотел бы знатьhow

Больше в ответе jjnguys:

Байт!

EDIT: Conclusion

Я нашел причину.

После предложения jjnguy я смог взглянуть на код другими глазами.

Виновный метод: StringBuilder.toString ()

A new String object is allocated and initialized to contain the character sequence currently represented by this object.

Doh! ...

    StringBuilder b = new StringBuilder("h");
    b.deleteCharAt( 0 );
    System.out.println( "" == b.toString() ); // prints false

Тайна разгадана.

Код использует StringBuilder для работы с постоянно растущей строкой. Оказывается, в какой-то момент кто-то сделал:

 public void someAction( String string ) { 
      if( "" == string ) {
           return;
       }

       deleteBankAccount( string );
 }

и использовать

 someAction( myBuilder.toString() ); // bug introduced. 

постскриптум Я читал слишком много CodingHorror в последнее время? Или почему я чувствую необходимость добавить сюда несколько забавных картинок с животными?

Спасибо за разъяснение Оскар! jjnguy
Как подсказывает мой ответ ... забудьте о том, что вы "подумали". Если вы хотите проверить, имеет ли что-то значение, совпадающее с пустой строкой, используйте equals. Вы пытаетесь зависеть от компилятора, который поддерживает ваши строки, что, как отмечает Джош Блох, не является инструментом для программистов :-). Увидеть ниже. Tom
(Могу ли я просто прервать, чтобы упомянутьString.isEmpty с 1.6. Спасибо.) Tom Hawtin - tackline
Несколько ответов теперь показывают простые манипуляции со строками, которые дают разные строковые объекты нулевой длины Сети и добро не имеют ничего общего с этими ответами. djna
if (value! = null & amp; value.length == 0) {...}, и вы должны использовать equals, потому что == для объектов сравнивает ссылки, т. е. являются ли они одним и тем же объектом. Хотя во время компиляции Java находит идентичные строки и заставляет их использовать одну и ту же ссылку (строки неизменны), во время выполнения легко создать пустые строки, которые имеют разные ссылки. JeeBee

Ваш Ответ

10   ответов
0
3

при компиляции Java-кода в байт-код или при запуске программы одни и те же строки будут ссылаться на один и тот же объект в большинстве случаев для экономии памяти. Так что иногда вам сходит с рук == сравнения строк. Но это оптимизация компилятора, на которую нельзя положиться.

Но иногда случается, что компилятор решает не выполнять эту оптимизацию, или у программы нет возможности увидеть, что строки совпадают, и внезапно проверка завершается неудачно, поскольку вы полагаетесь на некоторую базовую оптимизацию вуду, которая зависит от реализация используемого вами jvm и так далее.

Так что использование равных - это всегда хорошо. Для пустых строк есть другие возможности, такие как сравнение с длиной == 0 или, если вы не заботитесь о обратной совместимости, есть string.empty ().

10

Java Puzzlers Джошуа Блохом и Нилом Гафтером, и посмотрите на загадку 13 «Скотный двор» ... У него есть отличный совет по этому вопросу. Я собираюсь скопировать некоторый соответствующий текст:

& quot; Вы можете знать, что константы времени компиляции типаString являютсяinterned [JLS 15.28]. Другими словами, любые дваconstant expressions типаString которые обозначают одну и ту же последовательность символов, представлены идентичными ссылками на объекты ...Your code should rarely, if ever, depend on the interning of string constants. Interning был разработан исключительно для уменьшения объема памяти виртуальной машины, а не в качестве инструмента для программистов ... При сравнении ссылок на объекты вы должныuse the equals method in preference to the == operator если вам не нужно сравнивать идентичность объекта, а не значение. & quot;

Это из вышеупомянутой ссылки, которую я упомянул ... стр. 30 - 31 в моей книге.

Другими словами ... используйте равно, потому что это то, что вы имеете в виду ... и это то, что вы имеете в виду.
7

"abcde".substring(1,2) а также"zbcdefgh".substring(1,2) дать тот же объект String?

Они оба дают «равный» подстроки извлечены из двух разных строк, но кажется вполне разумным, что они являются разными объектами, поэтому == видит их как разные.

Теперь рассмотрим, когда подстрока имеет длину 0,substring(1, 1), Он дает строку нулевой длины, но неудивительно, что"abcde".substring(1,1) это другой объект от"zbcdefgh".substring(1,2) и, следовательно, по меньшей мере, один из них является объектом, отличным от «».

Я думаю, что это хороший ответ. очевидно, можно ожидать, что == возвращает false, а .equals возвращает true. И это то, что на самом деле спросил спрашивающий, а не "что такое разница между == и равно" что большинство других ответили ему
Я пытался объяснить это немного подробнее. При обучении я предпочитаю использовать вопросы, а не ответы. Думаю, это не так легко работает в тексте.
Хороший ответ.
Я делаю. В нем говорится, что даже если нет явных вызовов «новой строки», большой объем кода все равно создает новые строки ... substring, toUpperCase и т. Д.
Я не думаю, что эта аналогия действительно что-то проясняет
0

Javadoc заString.intern() есть хороший комментарий== против.equals().

Документация также разъясняет, что каждый строковый литералintern& APOS; d.

public String intern()

Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String literals are defined in §3.10.5 of the Java Language Specification

Returns: a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

21
"" == value // yields false

а также

"".equals( value ) // yields true

в любое время значение переменнойvalue не был интернирован. Это будет иметь место, если значение вычисляется во время выполнения. УвидетьРаздел JLS 3.10.5 Строковые литералы например код, иллюстрирующий это:

Thus, the test program consisting of the compilation unit (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

and the compilation unit:

package other;
public class Other { static String hello = "Hello"; }

produces the output:

true true true true false true

This example illustrates six points:

Literal strings within the same class (§8) in the same package (§7) represent references to the same String object (§4.3.1). Literal strings within different classes in the same package represent references to the same String object. Literal strings within different classes in different packages likewise represent references to the same String object. Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals. Strings computed at run time are newly created and therefore distinct. The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.
2

String.length() == 0.

Я бы тоже предложил это, если только строка не может быть нулевой. ;)
Это не отвечает на его вопрос ...
26
String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""

s == s2 оценивать как ложное.

Много кода порога создать новыйStrings не подвергая вызовnew String().

ха-ха. Грустная часть в том, что Firefox исправляет меня, но мне просто лень это исправлять. Мне нужно автозаполнение для обычного текста.
Нет, мне это нравится "Мы, Java-программисты, даже не знаем, как ЗАПИСАТЬ optimyz ... optimo ... послушайте, JIT делает это для нас, хорошо?"
Хотя это и не единственный правильный ответ, это привело меня к выяснению причины. Благодарю. .. OscarRyz
Правописание для словарей ...
Каждый раз, когда строка генерируется / создается / обрабатывается во время выполнения, она может произойти. Я предполагаю, что виртуальная машина может создать новую строку для пула строк по своему усмотрению, хотя для оптимизации было бы лучше использовать существующую строку.
0

if (value != null && value.length == 0) {
    // do stuff (above could be "== null ||"
}

Вы должны использоватьequals() так как== для объектов сравниваются ссылки, т. е. являются ли они одним и тем же объектом. Хотя во время компиляции Java находит идентичные строки и заставляет их использовать одну и ту же ссылку (строки являются неизменяемыми), во время выполнения легко создать пустые строки с разными ссылками, где == не удается для вашего типичного намеренияequals().

0

вы можете найти множество мест, где люди делают такую же ошибку:Google для файла: .java \ = \ = \ \ & quot; \ & quot;  Конечно, это может быть правильной идиомой в тщательно контролируемых обстоятельствах, но обычно это просто ошибка.

К сожалению, ссылка мертва
Да уж. Что ж, для приложений, использующих сеть, довольно часто происходит сбой, но для автономного кода это очень странно. Google-запрос хорош! :) OscarRyz

Похожие вопросы