11

我想确定 Java 中的某些内容:如果我有一个 Character 或 Integer 或 Long 之类的东西,我应该使用 equals 还是 == 就足够了?

我知道对于字符串,不能保证每个唯一字符串只有一个实例,但我不确定其他盒装类型。

我的直觉是使用equals,但我想确保我没有浪费性能。

4

9 回答 9

31

编辑:规范为装箱转换做了一些保证。从第 5.1.7 节

如果被装箱的值 p 是真、假、一个字节、一个在 \u0000 到 \u007f 范围内的字符,或者一个介于 -128 和 127 之间的 int 或短数字,则令 r1 和 r2 为任意两次装箱转换的结果p。r1 == r2 总是如此。

请注意,该实现可以使用更大的池。

真的会避免编写依赖于此的代码。不是因为它可能会失败,而是因为它并不明显——很少有人会那么了解规范。(我以前认为它是依赖于实现的。)

您应该使用equals或比较基础值,即

if (foo.equals(bar))

或者

if (foo.intValue() == bar.intValue())

请注意,即使保证自动装箱使用固定值,其他调用者也总是可以创建单独的实例。

于 2009-04-09T20:35:27.813 回答
8

如果要比较任何对象的值,请使用.equals().

即使(尤其是)这些对象是原始包装类型 Byte、Character、Short、Integer、Long、Float、Double 和 Boolean。

" ==" 只比较对象身份和你,这很少是你想要的。事实上,原始包装器永远不会满足您的需求。

==在以下两种情况之一中使用:

  1. 比较中涉及的所有值都是原始类型(最好不是浮点数)
  2. 您真的想知道两个引用是否引用同一个对象(这包括enums 的比较,因为该值绑定到对象标识)
于 2009-04-09T20:38:02.583 回答
5

Java 语言规范 5.1.7

如果被装箱的值 p 是真、假、一个字节、一个在 \u0000 到 \u007f 范围内的字符,或者一个介于 -128 和 127 之间的 int 或短数字,则令 r1 和 r2 为任意两次装箱转换的结果p。r1 == r2 总是如此。

和:

讨论

理想情况下,对给定的原始值 p 进行装箱将始终产生相同的参考。在实践中,使用现有的实现技术这可能是不可行的。上述规则是一种务实的妥协。上面的最后一个条款要求某些常见的值总是被装箱到无法区分的对象中。实现可能会懒惰地或急切地缓存这些。

对于其他值,此公式不允许程序员对装箱值的身份进行任何假设。这将允许(但不要求)共享部分或全部这些引用。

这确保了在最常见的情况下,行为将是期望的行为,而不会造成过度的性能损失,尤其是在小型设备上。例如,内存限制较少的实现可能会缓存所有字符和短整数,以及 -32K - +32K 范围内的整数和长整数。

因此,在某些情况下 == 会起作用,而在许多其他情况下则不会。始终使用 .equals 以确保安全,因为您无法(通常)授予实例的获取方式。

如果速度是一个因素(大多数 .equals 以 == 比较开头,或者至少它们应该)并且您可以保证它们是如何分配的并且它们适合上述范围,那么 == 是安全的。

一些 VM 可能会增加该大小,但假设语言规范指定的最小大小比依赖特定 VM 行为更安全,除非您真的真的需要这样做。

于 2009-04-09T21:06:29.223 回答
4
//Quick test
public class Test {
  public static void main(String[] args) {
    System.out.println("Are they equal? "+ (new Long(5) == new Long(5)));
  }
}

输出:

“他们相等吗?0”

回答:

不,他们不相等。您必须使用 .equals 或比较它们的原始值。

于 2009-04-09T20:37:19.090 回答
2

equals(Object o) 方法的实现几乎总是以

if(this == o) return true;

所以使用equals即使==是真的对性能影响不大。

我建议总是*equals在对象上使用该方法。

* 当然,在极少数情况下你不应该接受这个建议。

于 2009-04-09T22:21:12.523 回答
1

一般的答案是否定的,您不能保证对于相同的数值,您获得的 Long 对象是相同的(即使您限制自己使用 Long.valueOf())。

但是,通过首先尝试测试引用的相等性(使用 ==),然后如果失败,尝试 equals(),您可能会获得性能改进。这一切都取决于额外 == 测试和方法调用的比较成本......您的里程可能会有所不同,但值得尝试一个简单的循环测试,看看哪个更好。

于 2009-04-09T20:57:00.680 回答
1

值得注意的是,如果自动装箱值可用,它们将使用池化对象。这就是为什么 (Integer) 0 == (Integer) 0 但是 (Integer) 128 != (Integer) 128 for Java 6u13

于 2009-04-09T22:09:00.587 回答
1

我喜欢直观地看到结果:

  public static void main(String[] args)
  {
        Integer a = 126; //no boxed up conversion, new object ref
        Integer b = 126; //no boxed up conversion, re-use memory address
        System.out.println("Are they equal? " + (a == b)); // true
        Integer a1 = 140; //boxed up conversion, new object
        Integer b1 = 140; //boxed up conversion, new object
        System.out.println("Are they equal? " + (a1 == b1)); // false
        System.out.println("Are they equal? " + (new Long(5) == new Long(5))); // false
  }
于 2016-12-07T19:23:38.880 回答
0

==比较对象引用,同时equals(Object obj)比较对象是否相等。如果存在不止一个 equals 对象的实例,必须用于equals相等比较。

例子:

Integer i1 = new Integer(12345);
Integer i2 = new Integer(12345);

这些是不同的对象实例,但根据 Integer 的相等性相等,因此您必须使用equals(Object obj)

public enum Gender {
    MALE, FEMALE;
}

在这种情况下,只会存在一个实例,FEMALE因此==可以安全使用。

于 2009-04-09T21:13:31.140 回答