为什么 hashCode() 返回零会导致 List.minus() 返回一个空列表



给定类Foo,这个绝对糟糕的hashCode()实现:

class Foo {
    String name
    public int hashCode() {
        0
    }
    public boolean equals(Object obj) {
        if (obj == null) {
            return false
        }
        if (!(obj instanceof Foo)) {
            return false
        }
        Foo foo = (Foo) obj
        return this.name.equals(foo.name)
    }   
}

为什么以下断言失败?

Foo f1 = new Foo(name: 'Name 1')
Foo f2 = new Foo(name: 'Name 2')
Foo f3 = new Foo(name: 'Name 2')
assert ([f1, f2] - [f3]).size() == 1

minus()的结果是一个空列表。如果我将 hashCode() 实现切换到 return name.hashCode() ,断言就会通过。 无论使用哪种实现,contains()等方法都可以按预期工作。

我的问题不是如何实现更好的hashCode(),而是为什么minus()这样做。

这正是

文档中描述的减号行为:

创建一个列表,

由第一个列表的元素减去给定集合中出现的元素组成。

assert [1, "a", true, true, false, 5.3] - [true, 5.3] == [1, "a", false]

删除第二个列表中的每个元素。 在您的情况下,从 [f1,f2] 中删除所有 f3,其中所有 f1,f2 都相同,因此列表为空。

更精细的细节在DefaultGroovyMethods.minus中,然后在NumberAwareComperator中使用hashCode。 正如您已经发现的那样,有关于此的开放票证(https://jira.codehaus.org/browse/GROOVY-7158)。 所以在眼睛下面,hashCode在那里使用,行为是完全一致的......应该在那里使用吗? 也许不是,因为在某些情况下,它真的变得奇怪(例如 [[x:0,y:0]]-[[x:1,y:1]]==[])。

案例[f1,f2]-f3在代码中采用另一种路由,因此行为不同。

目前,我最好的猜测是,您将minus用于不可变类型(如上例所示),其中效果很好。 除此之外,而是使用集合。

Java 集合使用 hashCode/equals 的实现来确定对象相等性。hashCode的实现表明f1f2f3都是"相同的"。粗略地说:

[f1, f2] - [f3]

可以理解为

从列表中删除所有与 f3 相同的对象

因此,它会删除所有对象。

您似乎已经意识到这是一种可怕的实现hashCode的方式,因此实际上只是"垃圾输入,垃圾输出"的情况。

最新更新