【问题标题】:When does HashSet 'add' method calls equals? [duplicate]HashSet 'add' 方法何时调用等于? [复制]
【发布时间】:2013-01-08 10:03:23
【问题描述】:

我在 HashSet 比较中做了这个测试,equals 没有被调用

我想在 farAway=false 时考虑 equals (检查两点距离的函数)

完整的可编译代码,您可以对其进行测试,并说明为什么在此示例中未调用 equals。

public class TestClass{
     static class Posicion
    {
        private int x;
        private int y;

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Posicion other = (Posicion) obj;
            if ( farAway(this.x, other.x, this.y, other.y,5)){   
                return false;
            } 
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7; hash = 59 * hash + this.x; hash = 59 * hash + this.y;
            return hash;
        }

         Posicion(int x0, int y0) {
            x=x0;
            y=y0;
        }

        private boolean farAway(int x, int x0, int y, int y0, int i) {
            return false;
        }
    }

    public static void main(String[] args) {
        HashSet<Posicion> test=new HashSet<>();
        System.out.println("result:"+test.add(new Posicion(1,1)));
        System.out.println("result:"+test.add(new Posicion(1,2)));
    }
}

编辑

-有没有办法强制HashSet add to call equals?

【问题讨论】:

  • farAwayequals 中的调用是什么?你的班级违反了hashCodeequals 之间的合同!
  • @AlonsoDominguez 你有一个完整的测试,farAway 只是返回 false,以确保对象被视为相等,但永远不会调用 equals。
  • 您必须遵守hashCodeequals 之间的合同。这意味着当您的 equals 方法返回 true 时,您的 hashCode 必须返回相同的整数值。而这并没有发生在您的代码中。
  • @AlonsoDominguez 我明白,我试图避免 hashCode,并且只使用equals,我现在看到这是不可能的,感谢contract break,那么现在我知道equals 何时被调用,我仍然不知道equals 何时真正被调用
  • @HernánEche:您似乎想要做的事情可能非常棘手 - 您不仅必须遵守 hashCode() 的合同,还要遵守 equals() 的合同。 equals() 必须是可传递的。因此,如果您为 A 和 B 返回 true 因为它们彼此接近,而 B 和 C 因为它们彼此接近而返回,那么在检查 A 和 C 之间的相等性时,如果它们碰巧不接近,您将遇到问题.我认为你需要设计一个标准 HashSet 以外的容器/数据结构来做你想做的事情(如果你想做的事情首先是一个好主意)。

标签: java comparison equals hashset


【解决方案1】:

如果哈希码不同,则无需调用equals(),因为它保证返回false

这来自equals()hashCode() 上的一般contract

如果两个对象根据equals(Object) 方法相等,那么对这两个对象中的每一个调用hashCode 方法必须产生相同的整数结果。

现在您的班级正在违反该合同。你需要解决这个问题。

【讨论】:

  • 我需要equals被调用
  • 那么hashcode应该不一样。
  • @HernánEche:这没有任何意义。 HashSet 调用 equals() 以确保集合中不存储重复的对象。如果它可以确保不调用equals(例如,因为集合中还没有任何东西,或者因为集合中没有其他对象具有相同的哈希码),为什么它会调用equals()?还有其他什么对象?
  • @HernánEche:这两个对象返回不同的哈希码。根据hashCode() 的合同,图书馆(或任何人)可以认为这意味着对象不相等 - 如果哈希码不同,则无需调用equals()
  • @Hernán:如果哈希码不同,则对象不同,不会调用 equals。如果哈希码相同,则对象可能相等,因此调用 equals(Object) 以确保它们相同。
【解决方案2】:

如果您希望始终调用 equals(),只需始终在 hashCode() 中返回 0。这样,所有项目都具有相同的哈希码,并且纯粹与 equals() 进行比较。

public int hashCode() {
  return 0;
}

【讨论】:

  • 那您能解释一下您期望的结果吗?你的代码应该产生什么输出?
  • 功能是拒绝添加两个相似的对象,在这种情况下,平面上的两个点彼此靠近,但我可以通过数百种方式达到相同的功能,问题不在于解决单个问题,但即将理解什么时候 equals实际上是由HashMap调用的,或者equals从来没有通过它调用过。
  • 我刚刚测试了我的代码,它按预期工作。调用test.add(new Posicion(1,2) 返回false,因为调用了equals()。这有什么问题?
  • 我的测试错了,现在一切都清楚了,谢谢
  • 使用这个技巧有什么缺点吗?
【解决方案3】:

听起来 HashSet 不适合你。 听起来您想要一种比较两个位置的自定义方式。而不是说“两个位置完全相等吗?”。 相反,您应该考虑使用带有 Comparator 的 TreeSet。这样,您可以编写“IsWithinRangeComparator”并在那里进行范围检查。

【讨论】:

    【解决方案4】:

    如上所述,当对象相等时,它们的哈希码也应该相同。 您可以对哈希码计算进​​行简单的修复,如下所示。

     public int hashCode() {
    
    int hash = 7; hash = 59 * hash + this.x; hash = 59 * hash + this.y;
    boolean faraway=farAway(this.x, other.x, this.y, other.y,5);
    hash=59*hash+(faraway?1:0); //include faraway also as part of hashcode computation
    
     return hash;
    

    }

    【讨论】:

    • 当然不能使用 hashCode() 中的 farAway 方法,因为你没有其他对象可以比较。
    • @herman : Hashet 通过比较对象的哈希码来工作。如果哈希码不同,则无需调用 equals(),因为对象被认为不同,并且两个对象都将放入哈希集中。仅当哈希码相同时才调用 equals(),在这种情况下,将调用 equals() 来比较值
    • @Zenil 不,如果哈希码相同,则不会调用equals,因为这意味着对象相同!那我还是想知道,什么时候调用equals?
    • @Zenil:是的,你错过了一些东西。如果没有 other 对象,您想从哪里获取参数 other.x 和 other.y?
    • @jarnbjo:知道了。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 2017-06-05
    • 2020-10-10
    • 2016-03-19
    • 2019-11-19
    相关资源
    最近更新 更多