真正的答案
为什么有一个Comparator
接口,但不Hasher
和Equator
?
是,由Josh Bloch提供:
最初的Java API在紧迫的期限内很快就完成了,以迎接一个封闭的市场窗口。最初的Java团队做得非常出色,但是并非所有的API都是完美的。
问题仅仅在于Java的历史,与其他类似的问题,比如.clone()
VS Cloneable
。
tl; dr
主要是出于历史原因;当前的行为/抽象是在JDK 1.0中引入的,后来没有得到解决,因为实际上不可能通过保持向后代码兼容性来做到这一点。
首先,让我们总结一些众所周知的Java事实:
- 从一开始到今天,Java一直向后兼容,要求旧版API在新版本中仍受支持,
- 因此,JDK 1.0引入的几乎每种语言构造都可以应用到今天,
Hashtable
,.hashCode()
&.equals()
是在JDK 1.0,(实施哈希表)
Comparable
/ Comparator
是在JDK 1.2(Comparable)中引入的,
现在,它遵循:
- 在人们意识到存在比将它们放入超对象中更好的抽象之后,仍然要保持向后兼容性的同时
.hashCode()
,.equals()
要对这些不同的接口进行改型和实现几乎是毫无意义的,因为例如,每个 1.2的Java程序员都知道每个人Object
都有它们,并且保持物理状态以提供编译代码(JVM)兼容性-并为每个Object
真正实现它们的子类添加显式接口将使这一混乱(原文如此!)等于Clonable
一个(Bloch讨论了为什么Cloneable sucks很烂,也在EJ 2nd中进行了讨论和许多其他地方,包括SO),
- 他们只是将它们留在那儿,以便下一代拥有源源不断的WTF。
现在,您可能会问“这Hashtable
一切都有什么?”
答案是:hashCode()
/equals()
1995/1996年Java核心开发人员的合同和不太好的语言设计技能。
引用1996年Java 1.0语言规范 -4.3.2 The Class Object
,p.41:
声明这些方法equals
和的目的hashCode
是为了使哈希表受益,例如java.util.Hashtable
(§21.7)。equals方法定义了对象相等性的概念,该概念基于值而不是引用的比较。
(注意这个确切的说法已经改变了在以后的版本,说,报价:The method hashCode is very useful, together with the method equals, in hashtables such as java.util.HashMap.
,使其无法做出直接Hashtable
- hashCode
- equals
不读历史JLS连接!)
Java团队决定他们想要一个好的字典样式的集合,并创建了Hashtable
(到目前为止是个好主意),但是他们希望程序员能够以尽可能少的代码/学习曲线来使用它(糟糕!传入麻烦!)-并且,因为没有仿制药尚未[它的JDK 1.0毕竟],这将意味着,无论是每一个 Object
投产Hashtable
就必须明确地实现一些接口(和接口都还只是在其成立当年......没有Comparable
然而,即使!) ,这Object
会使许多人无法使用它-否则必须隐式实现某种哈希方法。
显然,出于上述原因,他们选择了解决方案2。是的,现在我们知道他们错了。...事后看来很聪明。轻笑
现在,hashCode()
要求具有它的每个对象都必须具有不同的equals()
方法 -因此很明显也equals()
必须将其放入Object
。
由于默认的上有效的方法的实现a
&b
Object
S被是多余的基本上是无用的(做a.equals(b)
等于给a==b
和a.hashCode() == b.hashCode()
大致相等,以a==b
还,除非hashCode
和/或equals
被重写,或者你GC几十万的Object
应用程序的生命周期在S 1) ,可以肯定地说,它们主要是作为备份措施提供的,并且使用方便。如果您打算实际比较对象或对它们进行散列存储,那么这就是我们始终熟知的事实的方式,即总是覆盖两者.equals()
和.hashCode()
。仅覆盖其中一个而不使用另一个是拧紧代码的好方法(通过糟糕的比较结果或疯狂的高存储桶碰撞值)-绕开它是初学者不断困惑和错误的根源(请搜索SO以查看) (适合您自己)和对经验丰富的人的持续干扰。
另外,请注意,尽管C#以更好的方式处理equals和hashcode,但Eric Lippert自己指出,他们在C#上犯的错误几乎与Sun在C#诞生之前对Java所犯的错误相同:
但是,为什么每个对象都应该能够对其自身进行哈希处理以插入到哈希表中呢?要求每个对象都能够做似乎很奇怪。我认为,如果今天我们从头开始重新设计类型系统,则散列可能会以不同的方式进行,也许使用IHashable
接口。但是,当设计CLR类型系统时,没有泛型类型,因此,通用哈希表需要能够存储任何对象。
当然1Object#hashCode
仍然可以冲突,但是要花些力气才能做到这一点,请参见:http : //bugs.java.com/bugdatabase/view_bug.do?bug_id=6809470和链接的错误报告以获取详细信息;/programming/1381060/hashcode-uniqueness/1381114#1381114更深入地介绍了此主题。
Person
实现预期equals
和hashCode
行为。然后,您将拥有一个HashMap<PersonWrapper, V>
。这是一个纯OOP方法并不完美的示例:并非对对象的每个操作都适合作为该对象的方法。Java的整个Object
类型是不同职责的集合体,只有getClass
,finalize
和toString
方法似乎可以被当今的最佳实践证明是合理的。