本当の答え
なぜそこにあるComparator
インターフェースが、ノーHasher
とはEquator
?
Josh Blochの好意による引用:
元のJava APIは、厳しい市場期間に対応するために、厳しい締め切りの下で非常に迅速に行われました。元のJavaチームは素晴らしい仕事をしましたが、すべてのAPIが完璧というわけではありません。
問題は、.clone()
vs などの他の同様の問題と同様に、Javaの歴史のみにありますCloneable
。
tl; dr
これは主に歴史的な理由によるものです。現在の動作/抽象化はJDK 1.0で導入されましたが、コードの後方互換性を維持することは事実上不可能であったため、後で修正されませんでした。
最初に、いくつかの有名なJavaの事実を要約しましょう。
- Javaは、当初から現在まで、下位互換性があり、新しいバージョンでもレガシーAPIを引き続きサポートする必要がありました。
- そのため、JDK 1.0で導入されたほぼすべての言語構成体は現在に至るまで存在し、
Hashtable
、.hashCode()
および.equals()
JDK 1.0で実装されました(Hashtable)
Comparable
/ Comparator
はJDK 1.2(Comparable)で導入されました。
今、それは次のとおりです。
- それは、改造することは事実上不可能&無意味だった
.hashCode()
と.equals()
人々はsuperobjectでそれらを置くより良い抽象化があるが実現した後、例えばので、まだ、後方互換性を維持しながら、明確なインターフェースに一つ一つ 1.2によって、Javaプログラマは、すべてのことを知っていたObject
それらを持っており、彼らが持っていました物理的にそこに留まってコンパイル済みコード(JVM)の互換性も提供します-そして、Object
それらを実際に実装するすべてのサブクラスに明示的なインターフェイスを追加すると、この混乱がClonable
1つに等しくなります(BlochがCloneable sucksの理由、例えばEJ 2ndおよびSOを含む他の多くの場所)、
- 将来の世代がWTFを絶え間なく入手できるように、それらをそのまま残しました。
さて、あなたは「Hashtable
このすべてに何があるのか」と尋ねることができますか?
答えは:hashCode()
/ equals()
契約と1996分の1995でコア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#はイコールとハッシュコードを少し上手に処理しますが、エリックリッパー自身は、C#が始まった数年前にSunがJavaで行ったのとほぼ同じ間違いをC#で行ったと述べています。
しかし、すべてのオブジェクトがハッシュテーブルに挿入するために自身をハッシュできる必要があるのはなぜですか?すべてのオブジェクトにできることを要求するのは奇妙なことのようです。今日、型システムをゼロから再設計している場合、おそらくIHashable
インターフェースを使用して、ハッシングが異なる方法で行われる可能性があると思います。しかし、CLR型システムが設計されたとき、ジェネリック型はなかったため、任意のオブジェクトを格納できるようにするために汎用ハッシュテーブルが必要でした。
1、もちろんObject#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アプローチが洗練されていない1つの例です。オブジェクトに対するすべての操作がそのオブジェクトのメソッドとして意味をなすわけではありません。JavaのObject
タイプ全体は、さまざまな責任の融合です。今日のベストプラクティスではgetClass
、finalize
とtoString
メソッドのみがリモートで正当化されるようです。