【问题标题】:Is it ok to compare immutable objects in Java using == instead of equals在Java中使用==而不是equals比较不可变对象是否可以?
【发布时间】:2012-06-10 17:39:30
【问题描述】:

考虑两个调用静态工厂方法 valueOf 的 Integer 类型的引用,如下所示:-

    Integer a = Integer.valueOf("10"); 
    Integer b = Integer.valueOf("10"); 

考虑到 Integer 是不可变的,是否可以使用 == 而不是使用 equals 方法比较 a 和 b。我猜 valueOf 方法确保只创建一个值为 10 的 Integer 实例,并为每个创建的值为 10 的 Integer 返回对该实例的引用。

一般来说,比较一个不可变类的两个引用是否可以通过使用 == 而不是 equals 来调用相同的静态工厂方法创建的?

编辑: Integer 类仅用作示例。我知道如果使用 == 进行比较,最多 127 的整数将返回 true。我需要知道的是,当我创建自己的不可变类时,例如使用方法 create() 的 MyImmutable,该方法将确保不会创建重复的 MyImmutable 对象,如果我比较使用 create 方法创建的 2 个 MyImmutable 引用是否可以使用 == 而不是等于。

【问题讨论】:

  • 请阅读this
  • 不变性只是意味着它们不能改变。需要特别注意缓存(没有new),以确保为相同的输入参数返回相同的值。
  • 您可以使用 == 代替 equals 用于未覆盖 equals 的类型。由于这在未来可能会发生变化,因此 equals 几乎总是更好。我唯一会说== 更好的是使用enum 之类的类型,它可能是null,在这种情况下使用== 可以简化代码。
  • 使用 == over equals 可以提高性能。如果不可变类中的方法保证它返回的引用使得 a.equals(b) 仅在 a == b 时为真,那么我看不出应该避免 == 的原因。

标签: java equals immutability


【解决方案1】:

不,这通常不安全。 == 运算符比较引用,而不是值。

使用 == 恰好适用于 -128 到 127 之间的整数,但不适用于其他整数。下面的代码演示了== 并不总是有效:

Integer a = Integer.valueOf(10); 
Integer b = Integer.valueOf(10); 
System.out.println(a == b);

true

Integer c = Integer.valueOf(1000); 
Integer d = Integer.valueOf(1000); 
System.out.println(c == d);

false

在线查看:ideone

这种行为的解释在于Integer.valueOf的实现:

public static Integer valueOf(int i) {
     final int offset = 128;
     if (i >= -128 && i <= 127) { // must cache
         return IntegerCache.cache[i + offset];
     }
     return new Integer(i);
 }

source

标准并不要求小输入(-128 到 127)的装箱整数为对象提供相等的引用。

5.1.7 拳击转换

如果被装箱的值 p 是 true、false、一个字节、一个在 \u0000 到 \u007f 范围内的字符,或者一个介于 -128 和 127 之间的 int 或短数字,那么让 r1 和 r2 是任何p的两次拳击转换。 r1 == r2 总是如此。

但是,该标准对超出此范围的整数没有做出此类保证。


一般来说,是否可以比较使用 == 而不是 equals 调用同一静态工厂方法创建的不可变类的两个引用?

如上所示,一般情况下它不会起作用。但是,如果您确保具有相同值的两个不可变对象始终具有相同的引用,那么是的,它可以工作。但是,您必须仔细遵守一些规则:

  • 构造函数不得公开。
  • 您通过静态方法创建的每个对象都必须被缓存。
  • 每次要求您创建对象时,您必须首先检查缓存,看看您是否已经创建了具有相同值的对象。

【讨论】:

  • 我的问题不是关于整数类。我希望得到一个一般性的答复。
  • 对不起。我错过了最后一部分。我通过我的 Galaxy S3 登录,它与台式机/笔记本电脑不同。 :)
  • 这确实是我一直在寻找的答案。非常感谢!
  • 我会在 Java 6 和 7 的更新版本中添加,缓存的最大 Integer 已调整/更改。这意味着 1000 甚至 10000 可能会或可能不会被缓存。我倾向于使用 -129 作为示例,因为仅更改了最大正数(或者您可以使用目前限制为 127L 的 Long)
【解决方案2】:

== 和 equals() 是根本不同的。

您应该阅读这篇文章以了解更多详细信息:

Difference between Equals/equals and == operator?

它与不可变对象无关。

【讨论】:

  • 我非常清楚equals和==之间的区别。在你说出显而易见的问题之前,请尝试理解我的问题。
  • 好的。我会给你一个例子,可以使用 == 进行比较。例如,我有一个具有用户对象的应用程序。我确保我只为每个用户实例化一个实例,即。当我调用 id 为 123 的用户时,在我的整个应用程序中始终只有一个用户对象,其 id 为 123。在这种情况下,使用 == 比较 2 个用户对象以查看它们是否是同一个用户是可以接受的。
  • 否则,使用 == 比较 2 个对象通常是不行的
  • 没问题。谢谢你的例子。
  • 我在阅读了明显的答案后失去了耐心。虽然你是对的。我会再次正确阅读答案。
【解决方案3】:

如果您的工厂方法针对相同的输入返回相同的对象,那么将它们与 == 进行比较是安全的。例如 String.intern 就是这样工作的。枚举也可以与 == 进行比较。但是 Integer.valueOf 仅在 -128 ... 127 范围内返回相同的对象(在默认配置中)。

Integer.valueOf(127) == Integer.valueOf(127)

但是

Integer.valueOf(128) != Integer.valueOf(128)

一般来说,你应该使用 equals 方法来比较任何对象。当对象有少量不同的值时,运算符 == 可用于提高性能。我不建议使用这种方法,除非您 100% 确定自己在做什么。

【讨论】:

  • 感谢您理解这个问题,而不是陈述显而易见的问题。所以我自己的不可变类,如果我能保证不存在重复项,是否可以使用 == 而不是 equals。
  • 如果你的工厂方法工作正常,没有人会做一些肮脏的事情,比如通过反射调用私有构造函数,那么一切都应该工作。因此,如果您有充分的理由这样做,请继续。
【解决方案4】:

不变性和平等性不一定相互关联。 == 比较引用相等,这意味着,它比较两个变量是否指向对象的同一个实例。相等意味着两个对象共享相同的值。不变性现在意味着您不能在构造对象后对其进行更改。

因此,您可能有两个不可变对象,它们表示相同的值(意思是,它们相等,因此 a.equals(b) 返回 true)但它们不是同一个实例。

我这里有一个小例子:

公共类 MyPoint { 私人int x; 私人int y; 公共MyPoint(int x,int y){ 这个.x = x; 这个.y = y; } 公共 int getX() { 返回 x; } 公共 int getY() { 返回 y; } @覆盖 公共布尔等于(对象 obj){ if (!(obj instanceof MyPoint)) 返回假; 我的点 p = (我的点) obj; 返回 this.x == p.x && this.y == p.y; } /** * @param 参数 */ 公共静态无效主要(字符串[]参数){ 我的点 p = 新的我的点 (2, 2); 我的点 q = 新的我的点 (2, 2); 我的点 r = q; System.out.println(p == q); System.out.println(p == r); System.out.println(q == r); System.out.println(p.equals(q)); System.out.println(p.equals(r)); System.out.println(q.equals(r)); } }

输出是: 错误的 错误的 真的 真的 真的 真的

MyPoint 是不可变的。初始化后,您无法更改其值/状态。但是,如您所见,myPoint 的两个对象可能相等,但它们可能不是同一个实例。

我认为您想到的是某种享元模式,其中每个可能的对象状态都只存在一个对象。享元通常也意味着这些对象是不可变的。

【讨论】:

  • 感谢蝇量级参考!
【解决方案5】:

它们不是同一个对象,所以== 不会是真的。对于对象,请注意安全并使用equals()。

【讨论】:

  • 从技术上讲,valueOf 缓存(默认情况下)-128 到 127,所以在这种情况下 a == b 将是真的。但是,是的,无论如何绝对要使用.equals。
  • 我说的是一个确保不创建重复项的类。在跳到答案之前,请尝试理解问题..
  • @bot:您最初的问题并未说明该类确保没有创建重复项。您在发布此答案后编辑了您的问题。
  • 我希望得到社区的再次确认。我希望有人告诉我以确保没有重复。我认为我的问题是为这个答案而痛苦,而不是明显的等于 vs == 辩论。
猜你喜欢
  • 2013-02-12
  • 1970-01-01
  • 2016-09-10
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-02
相关资源
最近更新 更多