比较字符串与空字符串 (Java)

发布于 2024-07-13 14:23:26 字数 312 浏览 4 评论 0原文

我有一个关于在 Java 中比较字符串和空字符串的问题。 如果我使用 ==equals 比较字符串与空字符串,有什么区别吗? 例如:

String s1 = "hi";

if (s1 == "")

或者

if (s1.equals("")) 

我知道应该将字符串(以及一般对象)与 equals 进行比较,而不是 ==,但我想知道这对于空字符串是否重要。

I have a question about comparing a string with the empty string in Java. Is there a difference, if I compare a string with the empty string with == or equals? For example:

String s1 = "hi";

if (s1 == "")

or

if (s1.equals("")) 

I know that one should compare strings (and objects in general) with equals, and not ==, but I am wondering whether it matters for the empty string.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

你在我安 2024-07-20 14:23:26
s1 == ""

不可靠,因为它测试引用相等性而不是对象相等性(并且 String 不是严格规范的)。

s1.equals("")

更好,但可能会出现空指针异常。 更好的是:

"".equals(s1)

没有空指针异常。

编辑:好的,问题是关于规范形式。 本文将其定义为:

假设我们有一些对象集合 S,
具有等价关系。 A
规范形式是通过指定给出的
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》中介绍了这个主题(最初在第一版中,他谈到了类型安全枚举模式,后来成为 Java 5 中的一个语言特性),您可以通过重载(私有)readResolve() 方法来绕过它。 但这很棘手。 类加载器也会影响这个问题。

无论如何,这是规范的形式。

s1 == ""

is not reliable as it tests reference equality not object equality (and String isn't strictly canonical).

s1.equals("")

is better but can suffer from null pointer exceptions. Better yet is:

"".equals(s1)

No null pointer exceptions.

EDIT: Ok, the point was asked about canonical form. This article defines it as:

Suppose we have some set S of objects,
with an equivalence relation. A
canonical form is given by designating
some objects of S to be "in canonical
form", such that every object under
consideration is equivalent to exactly
one object in canonical form.

To give you a practical example: take the set of rational numbers (or "fractions" are they're commonly called). A rational number consists of a numerator and a denomoinator (divisor), both of which are integers. These rational numbers are equivalent:

3/2, 6/4, 24/16

Rational nubmers are typically written such that the gcd (greatest common divisor) is 1. So all of them will be simplified to 3/2. 3/2 can be viewed as the canonical form of this set of rational numbers.

So what does it mean in programming when the term "canonical form" is used? It can mean a couple of things. Take for example this imaginary class:

public class MyInt {
  private final int number;

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

The hash code of the class MyInt is a canonical form of that class because for the set of all instances of MyInt, you can take any two elements m1 and m2 and they will obey the following relation:

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

That relation is the essence of canonical form. A more common way this crops up is when you use factory methods on classes such as:

public class MyClass {
  private MyClass() { }

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

Instances cannot be directly instantiated because the constructor is private. This is just a factory method. What a factory method allows you to do is things like:

  • Always return the same instance (abstracted singleton);
  • Just create a new intsance with every call;
  • Return objects in canonical form (more on this in a second); or
  • whatever you like.

Basically the factory method abstracts object creation and personally I think it would be an interesting language feature to force all constructors to be private to enforce the use of this pattern but I digress.

What you can do with this factory method is cache your instances that you create such that for any two instances s1 and s2 they obey the following test:

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

So when I say String isn't strictly canonical it means that:

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

But as others have poitned out you can change this by using:

String s3 = new String("blah");

and possibly:

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

So you can't rely on reference equality completely so you shouldn't rely on it at all.

As a caveat to the above pattern, I should point out that controlling object creation with private constructors and factory methods doesn't guarantee reference equality means object equality because of serialization. Serialization bypasses the normal object creation mechanism. Josh Bloch covers this topic in Effective Java (originally in the first edition when he talked about the typesafe enum pattern which later became a language feature in Java 5) and you can get around it by overloading the (private) readResolve() method. But it's tricky. Class loaders will affect the issue too.

Anyway, that's canonical form.

独﹏钓一江月 2024-07-20 14:23:26

这将取决于字符串是否是文字。 如果您使用 Then 创建字符串

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

It's going to depend on if the string is a literal or not. If you create the string with

new String("")

Then it will never match "" with the equals operator, as shown below:

    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

Basically, you want to always use equals()

如果没有你 2024-07-20 14:23:26

简短回答

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

我保证 s1 不为 null 并使用 isEmpty()。

注意:空字符串“”不是特殊的字符串,而是与任何其他“值”一样计数。

更长一点的答案

对 String 对象的引用取决于它们的创建方式:

使用运算符 new 创建的 String 对象始终引用单独的对象,即使它们存储相同的序列字符数如下:

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

使用运算符 = 后跟用双引号括起来的值 (= "value") 创建的 String 对象存储在 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”)创建的值的字符串也是如此,因此:

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

String equals 方法检查两者,这就是为什么可以安全地编写:

s1.equals("");

如果 s1 == null,则此表达式可能会抛出 NullPointerException,因此,如果您之前不检查 null,则编写更安全:

"".equals(s1);

另请阅读 如何在Java中比较字符串?

希望它可以帮助经验不足的用户,他们可能会发现其他答案有点太复杂。 :)

Short answer

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

I would assure s1 is not null and use isEmpty().

Note: empty string "" is not a special String, but counts as any other "value".

A little longer answer

References to String objects depend on the way they are created:

String objects created using the operator new always refer to separate objects, even if they store the same sequence of characters so:

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

String objects created using the operator = followed by a value enclosed whitin double quotes (= "value") are stored in a pool of String objects: before creating a new object in the pool, an object with the same value is searched in the pool and referenced if found.

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

The same is true for strings created enclosing a value whitin double quotes ("value"), so:

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

String equals method checks for both, that's why it is safe to write:

s1.equals("");

This expression may throw a NullPointerException if s1 == null, so, if you don't check for null before, it is safer to write:

"".equals(s1);

Please read also How do I compare strings in Java?

Hope it may help not so experienced users, who may find other answers a bit too complicated. :)

不必你懂 2024-07-20 14:23:26

这与你原来的问题有点偏离,但我总是

if(s1.length() == 0)

相信这相当于 1.6 中的 isEmpty() 方法。

It's a bit sideways from your original question, but there's always

if(s1.length() == 0)

I believe this is equivalent to isEmpty() method from 1.6.

红颜悴 2024-07-20 14:23:26
"".equals(s)

似乎是最好的选择,但 Apache commons lang 库中还包含 Stringutils.isEmpty(s)

"".equals(s)

Seems to be the best option, but there is also Stringutils.isEmpty(s) contained in the Apache commons lang library

寻找一个思念的角度 2024-07-20 14:23:26

字符串,是字符串,是字符串,无论是否为空字符串。 使用equals()

A string, is a string, is a string, whether it's the empty string or not. Use equals().

Spring初心 2024-07-20 14:23:26

给定两个字符串:

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

- 或 -

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

对两个对象执行的 == 运算符检查对象身份(如果两个运算符返回到同一个对象实例,则返回 true。)应用于 java.lang.Strings 的 == 的实际行为并不由于字符串驻留,看起来总是一致的。

在 Java 中,字符串interned(至少部分由 JVM 决定。)在某个时间点,s1 和 s2 可能会或可能不会被保留为相同的对象引用(假设它们具有相同的值)。因此 s1 == s2 可能会或可能不会返回 true,仅基于关于s1和s2是否都被拘留。

使 s1 和 s2 等于空字符串对此没有影响 - 它们仍然可能已被保留,也可能没有被保留。

简而言之,如果 s1 和 s2 具有相同的内容,== 可能返回 true,也可能不返回 true。 如果 s1 和 s2 具有相同的内容,则 s1.equals(s2) 保证返回 true。

Given two strings:

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

-or -

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

The == operator performed on two Objects checks for object identity (it returns true if the two operators return to the same object instance.) The actual behavior of == applied to java.lang.Strings does not always appear to be consistent because of String interning.

In Java, Strings are interned (at least partly at the discretion of the JVM.) At any point in time, s1 and s2 may or may not have been interned to be the same object reference (supposing they have the same value.) Thus s1 == s2 may or may not return true, based solely on whether s1 and s2 have both been interned.

Making s1 and s2 equal to empty Strings has no effect on this - they still may or may not have been interned.

In short, == may or may not return true if s1 and s2 have the same contents. s1.equals(s2) is guaranteed to return true if s1 and s2 have the same contents.

肥爪爪 2024-07-20 14:23:26

通过使用isEquals()方法来比较空字符串和String。

通过使用另一种方法CompareTo()方法。

By using isEquals() method to compare with the empty string and String.

By using another method CompareTo() method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文