Сравнение строки с пустой строкой (Java)

У меня вопрос о сравнении строки с пустой строкой в ​​Java. Есть ли разница, если я сравниваю строку с пустой строкой с == или equals? Например:

String s1 = "hi";

if (s1 == "")

или

if (s1.equals("")) 

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

Ответ 1

s1 == ""

не является надежным, поскольку он проверяет ссылочное равенство, а не равенство объекта (и String не является строго каноническим).

s1.equals("")

лучше, но может страдать от исключений нулевого указателя. Еще лучше:

"".equals(s1)

Исключения исключений нулевого указателя.

EDIT: Хорошо, вопрос был задан о канонической форме. Эта статья определяет его как:

Предположим, что мы имеем некоторое множество S объектов, с отношением эквивалентности. каноническая форма дается обозначением некоторые объекты S должны быть "в каноническом формы", так что каждый объект под рассмотрение эквивалентно точно один объект в канонической форме.

Чтобы дать вам практический пример: взять набор рациональных чисел (или "фракций", которые обычно называются). Рациональное число состоит из числителя и знаменателя (делителя), оба из которых являются целыми числами. Эти рациональные числа эквивалентны:

3/2, 6/4, 24/16

Rational nubmers обычно записываются так, что gcd (наибольший общий делитель) равен 1. Таким образом, все они будут упрощены до 3/2. 3/2 можно рассматривать как каноническую форму этого набора рациональных чисел.

Итак, что это значит при программировании, когда используется термин "каноническая форма"? Это может означать пару вещей. Возьмем, к примеру, этот воображаемый класс:

public class MyInt {
  private final int number;

  public MyInt(int number) { this.number = number; }
  public int hashCode() { return number; }
}

Хэш-код класса MyInt является канонической формой этого класса, потому что для множества всех экземпляров MyInt вы можете взять любые два элемента m1 и m2, и они будут подчиняться следующему отношению:

m1.equals(m2) == (m1.hashCode() == m2.hashCode())

Это отношение является сущностью канонической формы. Более общий способ получения этого урока - использовать методы factory для классов, таких как:

public class MyClass {
  private MyClass() { }

  public MyClass getInstance(...) { ... }
}

Экземпляры не могут быть напрямую созданы, потому что конструктор является закрытым. Это всего лишь метод factory. Что способ factory позволяет вам делать такие вещи, как:

  • Всегда возвращайте один и тот же экземпляр (абстрактный синглтон);
  • Просто создайте новое взаимодействие с каждым вызовом;
  • Возвращает объекты в канонической форме (подробнее об этом через секунду); или
  • что угодно.

В основном метод factory абстрагирует создание объекта, и лично я думаю, что это была бы интересная языковая функция, чтобы заставить всех конструкторов быть закрытыми для принудительного использования этого шаблона, но я отвлекаюсь.

Что вы можете сделать с помощью этого метода factory - это кеш файлы, которые вы создаете, так что для любых двух экземпляров s1 и s2 они подчиняются следующему тесту:

(s1 == s2) == s1.equals(s2)

Поэтому, когда я говорю, что String не является строго каноническим, это означает, что:

String s1 = "blah";
String s2 = "blah";
System.out.println(s1 == s2); // true

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

String s3 = new String("blah");

и, возможно:

String s4 = String.intern("blah");

Таким образом, вы не можете полностью полагаться на ссылочное равенство, чтобы не полагаться на него вообще.

Как предостережение к вышеуказанному шаблону, я должен указать, что управление созданием объекта с помощью частных конструкторов и методов factory не гарантирует, что ссылочное равенство означает равенство объектов из-за сериализации. Сериализация отключает механизм создания обычного объекта. Джош Блох рассказывает об этой теме в "Эффективной Java" (первоначально в первом выпуске, когда он рассказывал о типовом шаблоне enum, который позже стал языковой функцией в Java 5), ​​и вы можете обойти его, перегружая (private) метод readResolve(). Но это сложно. Классные загрузчики также повлияют на проблему.

Во всяком случае, эта каноническая форма.

Ответ 2

Это зависит от того, является ли строка литералом или нет. Если вы создаете строку с

new String("")

Тогда он никогда не будет соответствовать "" с оператором equals, как показано ниже:

    String one = "";
    String two = new String("");
    System.out.println("one == \"\": " + (one == ""));
    System.out.println("one.equals(\"\"): " + one.equals(""));
    System.out.println("two == \"\": " + (two == ""));
    System.out.println("two.equals(\"\"): " + two.equals(""));

-

one == "": true
one.equals(""): true
two == "": false
two.equals(""): true

В принципе, вы хотите всегда использовать equals()

Ответ 3

Это немного боком от вашего первоначального вопроса, но всегда

if(s1.length() == 0)

Я считаю, что это эквивалентно методу isEmpty() из 1.6.

Ответ 4

"".equals(s)

Кажется, это лучший вариант, но есть также Stringutils.isEmpty(s), содержащиеся в библиотеке Apache commons lang

Ответ 5

Строка, является строкой, является строкой, является ли она пустой строкой или нет. Используйте equals().

Ответ 6

Короткий ответ

s1 == ""         // No!
s1.equals("")    // Ok
s1.isEmpty()     // Ok: fast (from Java 1.6) 
"".equals(s1)    // Ok: null safe

Я бы заверил, что s1 не является нулевым и использует isEmpty().

Примечание: пустая строка "не является специальной строкой, а считается как любое другое" значение".

Более длинный ответ

Ссылки на объекты String зависят от способа их создания:

Строковые объекты, созданные с помощью оператора new, всегда относятся к отдельным объектам, даже если они сохраняют одну и ту же последовательность символов, поэтому:

String s1 = new String("");
String s2 = new String("");
s1 == s2 // false

Объекты String, созданные с помощью оператора =, сопровождаемые значением, заключенным в двойные кавычки whitin (= "значение" ), хранятся в пуле объектов String: перед созданием нового объекта в пуле объект с таким же значением выполняется в пуле и ссылается, если найден.

String s1 = ""; // "" added to the pool
String s2 = ""; // found "" in the pool, s2 will reference the same object of s1
s1 == s2        // true

То же самое верно для созданных строк, включающих двойные кавычки value whitin ( "value" ), поэтому:

String s1 = "";  
s1 == "";        //true

Строка равно методу проверки для обоих, поэтому безопасно писать:

s1.equals("");

Это выражение может вызывать исключение NullPointerException, если s1 == null, поэтому, если вы не проверяете значение null раньше, безопаснее писать:

"".equals(s1);

Прочтите также Как сравнить строки в Java?

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

Ответ 8

Учитывая две строки:

String s1 = "abc";
String s2 = "abc";

-or -

String s1 = new String("abc");
String s2 = new String("abc");

Оператор ==, выполняемый в двух объектах, проверяет идентификатор объекта (он возвращает true, если оба оператора возвращаются к одному экземпляру объекта.) Фактическое поведение ==, примененное к java.lang.Strings, не всегда выглядит согласованно из-за интернирования строк.

В Java строки интернированы (по крайней мере, частично по усмотрению JVM.) В любой момент времени s1 и s2 может быть или не быть интернированным, чтобы быть одной и той же ссылкой на объект (если они имеют одинаковое значение.) Таким образом, s1 == s2 может возвращать true или не возвращать true, основываясь исключительно на том, были ли интернированы s1 и s2.

Сделать s1 и s2 равными пустым. Строки не влияют на это - они все еще могут быть или не быть интернированы.

Короче говоря, == может или не может возвращать true, если s1 и s2 имеют одинаковое содержимое. s1.equals(s2) гарантированно возвращает true, если s1 и s2 имеют одинаковое содержимое.