У меня вопрос о сравнении строки с пустой строкой в 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?
Надеюсь, что это может помочь не настолько опытным пользователям, которые могут найти другие ответы слишком сложно.:)
Ответ 7
Используйте String.isEmpty() или StringUtils.isEmpty(String str), если вам нужна нулевая проверка.
Ответ 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 имеют одинаковое содержимое.