HashMap について読みました。ある時点でそれが指摘されました:
「不変性により、さまざまなキーのハッシュコードをキャッシュできるため、検索プロセス全体が非常に高速になり、Stringおよびさまざまなラッパークラス(例:
Integer
)がJava Collection APIとても良いHashMap
キーです。」
よくわからない…なんで?
String#hashCode
:
private int hash;
...
public int hashCode() {
int h = hash;
if (h == 0 && count > 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
String
の内容は決して変更されないため、クラスの作成者は、一度計算された後のハッシュをキャッシュすることを選択しました。このようにして、同じ値を再計算して時間を無駄にすることはありません。
リンクされたブログエントリを引用:
適切なequals()とhashcode()の実装を持つ最終オブジェクトは、完全なJava HashMapキーとして機能し、衝突を減らすことでJava hashMapのパフォーマンスを向上させます。
final
とequals()
の両方がハッシュの衝突とどのように関係しているかはわかりません。この文は、記事の信頼性について私の疑念を引き起こします。それは独断的なJava「知恵」のコレクションのようです。
不変性により、さまざまなキーのハッシュコードをキャッシュできるため、検索プロセス全体が非常に高速になり、文字列やさまざまなラッパークラス、たとえばJava Collection APIによって提供される整数)が非常に優れたHashMapキーであることが示唆されます。
この文の解釈は2つ考えられますが、どちらも間違っています。
HashMap
は不変オブジェクトのハッシュコードをキャッシュします。これは正しくありません。マップは、オブジェクトが「不変」であるかどうかを確認することができません。したがって、本当にクレイジーで、実際にList
をHashMap
およびのキーとして使用することにした場合、ハッシュ値はリストのIDではなく、内容に依存します。変更のたびにキャッシュされたハッシュ値を無効にすることを決定できます。これにより、ハッシュ計算の数をリストの変更の数に制限します。
とても簡単です。 immutableオブジェクトは時間の経過とともに変化しないため、ハッシュコードの計算を1回実行するだけで済みます。再度計算すると同じ値になります。そのため、ハッシュコードをコンストラクターで(または遅延して)計算してフィールドに格納するのが一般的です。次に、hashcode
関数はフィールドの値のみを返します。これは実際に非常に高速です。
基本的に不変はJavaでクラスを拡張不可能にすることによって達成され、オブジェクトのすべての操作はオブジェクトの状態を変更しないことが理想的です。replace()のようなStringの操作を見ると、操作している現在のオブジェクトの状態を変更するのではなく、文字列を置き換えた新しいStringオブジェクトを提供します。したがって、そのようなオブジェクトをキーとして保持している場合、状態は変更されないため、ハッシュコードも残ります。したがって、ハッシュコードをキャッシュすると、取得時にパフォーマンスが向上します。
ハッシュマップは番号の付いたボックスの大きな配列と考えてください。番号はハッシュコードで、ボックスは番号順になっています。
オブジェクトが変更できない場合、ハッシュ関数は常に同じ値を再現します。したがって、オブジェクトは常にそのボックス内に留まります。
次に、変更可能なオブジェクトを想定します。これはハッシュに追加した後で変更されたため、今では誤ってミスタードーと結婚し、現在はドーと呼ばれているジョーンズ夫人のように、間違ったボックスに配置されていますが、多くのレジスタではまだジョーンズと呼ばれています。
ハッシュテーブルは、オブジェクトのハッシュコードがテーブルに格納されている間は変更できない場合にのみ機能します。これは、ハッシュコードが、テーブル内にある間に変更される可能性があるオブジェクトのいかなる側面も考慮できないことを意味します。オブジェクトの最も興味深い側面が変更可能な場合、それは以下のいずれかを意味します。
ハッシュコードは、オブジェクトの興味深い側面のほとんどを無視する必要があるため、多くのハッシュ衝突が発生します。
ハッシュテーブルを所有するコードは、その中のオブジェクトが、ハッシュテーブルに格納されている間、それらを変更する可能性のあるものに公開されないようにする必要があります。
JavaハッシュテーブルによってクライアントがEqualityComparerを提供できるようになった場合(.NET辞書と同じように)、ハッシュテーブル内のオブジェクトの特定の側面が予期せず変更されないことを知っているコードは、ハッシュを使用できますこれらの側面を考慮したコードですが、Javaでそれを実現する唯一の方法は、ハッシュコードに格納されている各アイテムをラッパーでラップすることです。このようなラッピングは、ただし、ラッパーはEqualityComparer
ができない方法でハッシュ値をキャッシュすることができ、さらに、等値関連の情報をキャッシュすることができるため[たとえば、格納されているものがネストされたコレクションである場合、複数のハッシュコードを計算し、要素の詳細な検査を行う前にすべてのハッシュコードが一致することを確認する価値があるかもしれません]。