【问题标题】:Transitive nature of equals methodequals 方法的传递性
【发布时间】:2011-10-08 13:45:27
【问题描述】:

equals(object) 方法的契约指定了要遵循的 4 个属性:自反、对称、传递和一致。虽然我理解不遵循 Reflexive、Symmetric 和 Consistent 的危险,并且绝对同意遵循传递性的好处,但我想知道如果它违反传递性属性会带来什么危害?

具体来说,哪个 Java 库(或各种第三方库)需要对 equals 的依赖才能正常工作?据我了解,如果其他 3 个属性得到很好的实现,Collections 框架就会起作用。

【问题讨论】:

    标签: java


    【解决方案1】:

    假设三个对象 a,b,c 与

    a == a, b == b, c == c (reflexive)
    a == b, b == a
    b == c, c == b
    a != c, c != a
    

    (伪代码,x == y 代表x.equals(y))。

    现在,让我们将对象添加到集合中:

    Set s = new HashSet(); // Set implementation doesn't matter
    s.add(b); // s = [b]
    s.add(a); // s doesn't change, because a == b
    s.add(c); // s doesn't change, because c == b
    

    相反,如果我们以不同的顺序添加它们:

    Set s = new HashSet();
    s.add(a); // s = [a]
    s.add(b); // s doesn't change, because b == a
    s.add(c); // s = [a,c], because c != a
    

    这显然是违反直觉的,并且与人们期望的一组行为不符。例如,这意味着两个集合的并集(即s.addAll(someOtherSet) 之后的 s 状态)可能取决于 someOtherSet 的实现(元素的顺序)。

    【讨论】:

    • 这是一个真正的好 :) 虽然你宁愿想在第一个块中写“.equals”而不是“==”;)
    • @Gandalf 我不会。我认为这比必要的要复杂得多;)很好的例子。
    • @Gandalf 好的,写了equals。我理解为什么 Java 人忽略了它,但它并不能完全使代码可读。等价关系的标准与语言无关。
    • @Voo 你是对的。回复==并为吹毛求疵的魔术师添加评论。
    【解决方案2】:

    目前我不知道有没有传递性问题的 Java API。 (我仍在思考一个例子)。

    但除此之外,等式需要传递性,因为这是等式关系的数学代数定义。 http://en.wikipedia.org/wiki/Equality_(mathematics)

    如果不存在传递性,则该方法不得称为 equals,因为考虑到人们在听到/阅读“平等”时的期望,这会产生误导。这将与最小惊讶原则相矛盾。 http://en.wikipedia.org/wiki/Principle_of_least_astonishment

    [编辑]

    有趣的是,严格来说,java equals 方法定义的“相等”不是相等,而是更一般的等价关系http://en.wikipedia.org/wiki/Equivalence_relation,因为根据 java,不同的对象也可以“相等”,因此与真正相等所需的反对称性质相矛盾。

    结论:

    .equals 在 Java 中是等价关系(仍然需要传递性)

    == 在 Java 中是一个相等(或身份)关系

    【讨论】:

      【解决方案3】:

      考虑对象 a == b == c 和 a != c(非传递相等)

      第一个问题是 hashcode() 合约,如果对象相等,则要求 hashcode 相等。而且您将能够将 a 和 c 添加到同一个集合中 - 这可能会在意想不到的地方导致微妙的问题

      【讨论】:

      • hashCode 不是问题:int hashCode() {return 0;} 满足 x == y ⇒ x.hashCode() == y.hashCode() 的含义。
      • 我不明白为什么这是个问题。如果 a == b 和 b == c 则 a 和 b 具有相同的哈希码,b 和 c 具有相同的哈希码,因此 a 和 c 具有相同的哈希码。仅仅因为 a != c 并不意味着他们不能共享哈希码。
      • 在听取了@joshbloch 的一些谈话(他在 Java 和其他东西中开发集合框架方面做了很多工作)之后,我在编写 hashCode 和 equals 时非常谨慎 - 违反他们的合同可能会导致非常奇怪的结果
      【解决方案4】:
      Integer a = new Integer(1);
      Integer b = new Integer(1);
      

      a == 1 为真,b == 1 为真,但a == b 不为真。

      【讨论】:

      • 这既不能回答问题,也不能解释什么。
      • Java 是我所知道的唯一一种具有原始类型的盒子类型的语言。而且我从未见过另一种语言使 1 != 1 发生,不是 python,不是 javascript,不是 c#,不是 c,不是 go,不是 rust。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-18
      • 1970-01-01
      • 2013-07-28
      • 2014-05-27
      相关资源
      最近更新 更多