Сравнение строки с пустой строкой (Java) - PullRequest
31 голосов
/ 10 февраля 2009

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

String s1 = "hi";

if (s1 == "")

или

if (s1.equals("")) 

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

Ответы [ 8 ]

67 голосов
/ 10 февраля 2009
s1 == ""

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

s1.equals("")

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

"".equals(s1)

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

РЕДАКТИРОВАТЬ: Хорошо, вопрос был задан о каноническая форма . Эта статья определяет это как:

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

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

3/2, 6/4, 24/16

Рациональные числа обычно пишутся так, что 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())

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

public class MyClass {
  private MyClass() { }

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

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

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

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

Что вы можете сделать с помощью этого фабричного метода, так это кешировать ваши экземпляры, которые вы создаете так, что для любых двух экземпляров 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");

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

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

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

26 голосов
/ 10 февраля 2009

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

new String("")

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

    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 ()

10 голосов
/ 10 февраля 2009

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

if(s1.length() == 0)

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

9 голосов
/ 10 февраля 2009
"".equals(s)

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

8 голосов
/ 21 мая 2015

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

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: перед созданием нового объекта в пуле объект с тем же значением ищется в пуле и ссылается, если найден.

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

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

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

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

s1.equals("");

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

"".equals(s1);

Пожалуйста, прочитайте также Как сравнить строки в Java?

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

6 голосов
/ 10 февраля 2009

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

3 голосов
/ 21 декабря 2010

Используйте String.isEmpty () или StringUtils.isEmpty (String str) , если вам нужна проверка на ноль.

0 голосов
/ 11 февраля 2009

Дано две строки:

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

-или -

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

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

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

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

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

...