Python和Java等语言基于对象的标识符提供默认的散列和等式实现。这允许在基于散列的数据结构(如集合)中使用任何新的类类型。
根据我的经验,这是一个陷阱。开发人员编写他们的对象,然后遇到奇怪的(但技术上正确的)行为,例如:
>>> len(set([Foo(5), Foo(5)]))
2
这种默认行为背后的基本原理是什么?什么时候基于身份的哈希和平等有用?这有什么好处,只是抛出一个未实现的异常来强制程序员创建对这个对象有意义的行为?
答案 0 :(得分:1)
你自己回答:
这允许在基于散列的数据结构(如集合)中使用任何新的类类型。
关于这些方法的默认实现:
默认是使用对象标识符,因为没有其他合理的默认值。如果默认值不好,则必须覆盖它。 只有您了解有关课程的详细信息。
可能还有其他情况需要不同的行为(想象一个类Person(age)
):
len(set([Person(30), Person(45), Person(30)]))
非常有意义,因为它是3。
然而,这只是一个幸运的巧合。
答案 1 :(得分:1)
显然,我们只能推测为什么方法是这样设计的。和许多语言一样,有时设计师希望他们可以回去改变他们设计某个特征的方式。
例如,有些人会争辩说,如果从头开始重新设计Java,可能不会包含checked exceptions之类的内容。或者,Object类不会包含wait
,notify
方法等。因此,至少在涉及Object.hashCode()
方法时,该功能是否有可能如果今天从头开始重新设计Java会有不同的设计吗?
涉及equals
方法。我认为对于拥有它的每个对象都是完全合理的,并且对于它的默认实现来执行引用相等。
至于hashCode
方法,那么我必须同意OP一样,经常使用它很尴尬。有时会有一些我们从不想在哈希表中使用的对象,但我们希望重新定义逻辑以获得相等性。所以,要么重新定义hashCode
(浪费精力)只是为了确保没有人在以后的哈希表中不正当地使用它,或者你不做,然后你就会有令人讨厌的惊喜当有人决定将对象粘贴在哈希表中时。
就个人而言,我发现C#编译器团队的开发人员Eric Lippert发现这个article非常具有启发性,而且讨论也非常适用于Java。特别是,他说以下(最后一段很有趣):
GetHashCode用于什么?
设计只对一件事有用:将对象放在哈希表中。因此这个名字。
为什么我们首先在Object上使用此方法?
类型系统中的每个对象都应提供GetType方法,这是完全合理的。数据描述自身的能力是CLR类型系统的关键特征。并且每个对象都应该有一个ToString是有道理的,因此它可以打印出自己的表示形式作为字符串,以便进行调试。似乎合理的是,对象应该能够将自己与其他对象进行比较以获得相等。但是为什么每个对象应该能够自己散列以插入哈希表呢?看起来很奇怪,要求每个对象都能够做到。
我认为如果我们今天从头开始重新设计类型系统,可能会以不同的方式进行散列,也许使用IHashable接口。但是当设计CLR类型系统时,没有泛型类型,因此通用哈希表需要能够存储任何对象。
答案 2 :(得分:0)
我想到了更多,我想我有答案。
在Java和Python中,基础对象都不是抽象的
new Object() // Java
object() # Python
为了做到这一点,实现equals和hashcode是一个好主意,在这种情况下身份是正确的。因此,默认值更多......这是继承的结果。
我抛出异常的世界需要某种模型,其中基类可以定义一些东西但强制子类来实现它们自己。