A Pythonで 行って、 または JavaScript で、PHP でアソシエティブアレイヤー、 ハッシュマップは、ほぼすべての高レベルのプログラミング言語で実装されています。そして彼らは素晴らしいです! 誰がストレージして、その後、絶え間なくデータにアクセスしたくないですか? あなたが大規模なデータセットやLeetcodeの問題で作業しているかどうか、非常に頻繁にこのデータ構造は救援に来ます。 しかし、彼らは正確に何ですか、そして彼らは帽子の下でどのように働くのですか? この記事では、私たちはこれらの質問に答えようとします。 dict map Object Map Dictionary<TKey, TValue> ハッシュマップって何? 高レベルのハッシュマップまたはハッシュテーブルは、アソシエーション数列抽象データ型を実装するデータ構造で、キーを値にマッピングできる構造です。キーはマップ内の値をユニークに識別するために使用され、値は保存されているデータです。 実際、これらの操作の平均時間複雑性はO(1),つまり、これらは常時実行できる! この機能はハッシュマップをプログラミングで最も多く使用されるデータ構造にするが、後で見るように、これにはいくつかの警告があります。 これらの操作の最悪の時間複雑性は、特定のシナリオで起こりうるO(n)であり、内部について知るほど、これらのシナリオを避ける可能性が高くなります。 」によると、The : Wikipediaの記事 a hash table is a data structure that implements an associative array, also called a dictionary or simply map; an associative array is an abstract data type that maps keys to values. A hash table uses a hash function to calculate an index, also called a hash code, into an array of buckets or slots, from which the desired value can be found. ハッシュテーブルは、アソシエイティブな数値を実装し、またハッシュコードと呼ばれるインデックスをバケットまたはスロットの数列に計算します。 a hash table is a data structure that implements an associative array, also called a dictionary or simply map; an associative array is an abstract data type that maps keys to values. A hash table uses a hash function to calculate an index, also called a hash code, into an array of buckets or slots, from which the desired value can be found. ハッシュテーブルは、アソシエイティブな数値を実装し、またハッシュコードと呼ばれるインデックスをバケットまたはスロットの数列に計算します。 だから、一歩振り返ってハッシュマップのコンポーネントを見てみましょう。 ハッシュ機能とは何ですか? A hash function is a function that takes an input (or "key") and typically returns an integer that is used to index the data in the hash map. The key is transformed into an integer, which is then used to determine the index in the underlying array where the value is stored. ハッシュ関数は、入力(または「キー」)を取って、通常、ハッシュマップ内のデータをインデックスするために使用される整数を返します。 良いハッシュ機能には、以下の特性があります。 Deterministic: 同じ入力は常に同じ出力を生成します。 統一配布:ハッシュ関数は、ハッシュテーブル全体でキーを均一に配布し、衝突を最小限に抑える必要があります。 スピードコンピューティング:ハッシュ関数は、大きな入力の場合でも、スピードコンピューティングでなければなりません。 可能なキーのスペースは、通常、ハッシュコードのスペースよりもはるかに大きい(しばしば無限)ということです。これは、異なるキーが同じハッシュコードを生成する可能性があることを意味します。 シンプルなハッシュ機能の例は、モジュール操作で、ハッシュテーブルのサイズで割り当てられた場合にキーを取り、残りを返します。例えば、10サイズのハッシュテーブルと23のキーがある場合、ハッシュコードは , つまり、キー 23 に関連付けられた値は、ベースのマレージのインデックス 3 に格納されます. And if the key is 33, the hash code would be この場合、両方のキーは、マッサージ内の同じインデックスにマッピングします。 23 % 10 = 3 33 % 10 = 3 バケットって何? バケットは、キー値のカップルが格納されているハッシュテーブルのスロットです。 衝突の場合、2つの異なるキーが同じハッシュコードを生成する場合、バケットは複数のキー値のカップルを格納することができます。 この図は、これらのすべてがどのように機能するかを示しています: ♪ ここでは、ハッシュ関数がどのようにインデックスにキーをマッピングするかを見ることができます。キー23と33は3の同じハッシュコードを生成するので、それらが同じボックスに保存されていることを意味します。ボックスはその後、両方のキー値のペアを保存することができますが、値を取得するとき、正しいものを探すためにボックス内のキーをチェックする必要があります。これは、最悪の場合、複雑さがO(n)に増加することができ、多くの (またはすべての)キーが衝突し、同じボックスに保存されている場合です。 ロード・ファクター 負荷因子は、ハッシュテーブルがどれほど満たされているかを測定するものである。それは、ハッシュテーブル内の要素の数で、ベースのアレイのバケット(またはスロット)の数で割り当てられる。より高い負荷因子は、ハッシュテーブルにバケットの数に関連してより多くの要素があることを意味し、これはより多くの衝突やより遅いパフォーマンスにつながる可能性があります。 衝突決議 2 つのキーが同じハッシュコードを生成する場合、ハッシュマップで衝突を処理するためのいくつかの戦略があります。 : In this method, each bucket contains a linked list (or another data structure) of all key-value pairs that hash to the same index. When a collision occurs, the new key-value pair is simply added to the list in the appropriate bucket. This is the most common method for handling collisions. Chaining : Average O(1) for all operations, worst-case O(n) if all keys hash to the same bucket Complexity : Simple to implement, handles high load factors well, deletion is straightforward Pros : Extra memory overhead for pointers, poor cache performance due to scattered memory access Cons Open Addressing: This method, when a collision occurs, the hash table searches for the next available slot in the array to store the new key-value pair. 次の利用可能なスロットを見つけるためのいくつかのテクニックがあります。 : 衝突が発生した場合、アルゴリズムは空のスロットを見つけるまで、次のスロットをチェックします。 Linear Probing 複雑性:平均O(1),最悪の場合O(n) 利点:シンプルな実装、近隣アクセスのための良いキャッシュパフォーマンス マイナス: Primary clustering (consecutive occupied slots), performance degradations with clustering 次のスロットをチェックする代わりに、元のインデックスから増加する距離 (1, 4, 9, など)でスロットをチェックします。 Quadratic Probing 複雑性:平均O(1),減少したクラスタリングのために線形探査よりも優れている 利点:Linear probing に比べて、primary clustering を削減し、まだキャッシュフレンドリー マイナス: 二次集積(同じハッシュを持つキーは同じサンド シーケンスに従う)、すべてのスロットを訪問しない場合があります。 次のスロットに常に移動する線形スロットと異なり、固定シーケンスを使用する平方スロットと異なり、ダブルハッシュは各キーのためのユニークなステップサイズを計算します。 どこ 2番目のハッシュ関数は、テーブルサイズに比較的優れた値を返し、すべてのスロットを閲覧できるようにしなければなりません。 そして , then we would probe indices 7, 10, 13, 16, etc. Double Hashing index = (hash1(key) + i * hash2(key)) % table_size i hash1(key) = 7 hash2(key) = 3 : Average O(1), best performance among open addressing methods Complexity : Minimizes clustering, uniform distribution of probe sequences, visits all slots when properly implemented Pros : More complex implementation, requires computing two hash functions, and slightly more overhead per operation Cons : If the load factor becomes too high, the hash table can be resized and all existing key-value pairs can be rehashed into the new table. This helps to maintain efficient performance as the number of elements grows. Rehashing : O(n) for the rehashing operation itself, but amortizes to O(1) per insertion over time Complexity : Maintains optimal performance by keeping the load factor low, prevents performance degradation Pros : Temporary performance spike during rehashing, requires additional memory during the resize operation Cons これらの方法のそれぞれは、複雑さ、パフォーマンス、およびメモリ使用の点で独自の妥協点を持っています.The choice of collision resolution strategy can have a significant impact on the overall performance of the hash map. 以下は、それぞれの衝突解消方法の利点と欠点の簡単な概要です。 Feature Chaining Linear Probing Quadratic Probing Double Hashing Average Time Complexity O(1) O(1) O(1) O(1) Worst-case Time Complexity O(n) O(n) O(n) O(n) Memory Overhead High (pointers) Low Low Low Cache Performance Poor Good Good Moderate Implementation Complexity Simple Simple Moderate Complex Clustering Issues None Primary clustering Secondary clustering Minimal Load Factor Tolerance High (>1.0) Low (<0.7) Low-Medium (<0.7) Medium (<0.8) Deletion Complexity Simple Complex (tombstones) Complex (tombstones) Complex (tombstones) Space Efficiency Lower Higher Higher Higher Performance Degradation Gradual Rapid at high load Moderate at high load Slow at high load Hash Function Requirements One One One Two Best Use Cases Unknown load factors, frequent deletions Cache-friendly applications, low load Better than linear, moderate load High performance, predictable load Average Time Complexity O(1) O(1) O(1) O(1) Worst-case Time Complexity O(n) O(n) O(n) O(n) Memory Overhead トップ > Pointers 低い 低い 低い Cache Performance 貧しい 良い 良い 適度 Implementation Complexity シンプル シンプル 適度 複雑 Clustering Issues 誰も 主なクラスター 二次集団化 最小限 Load Factor Tolerance トップ > 1.0 低い(<0.7) 低水準(<0.7) メディア(<0.8) Deletion Complexity シンプル コンプレックス(墓石) コンプレックス(墓石) コンプレックス(墓石) Space Efficiency 低い より高い より高い より高い Performance Degradation 段階化 急速な高負荷 Moderate at High Load (高負荷) ・Low at High Load Hash Function Requirements 1位 1位 1位 2位 Best Use Cases 未知の負荷因子、頻繁な削除 キャッシュフレンドリーなアプリケーション、低負荷 Better Than Linear, Moderate Load よりマシ 高性能、予測可能な負荷 いくつかの現実世界の例 プログラミング言語の実装 多くのプログラミング言語にはハッシュマップが組み込まれ、これらの実装はしばしば上記のテクニックの組み合わせを使用して効率的なパフォーマンスを提供し、衝突を効果的に処理します。 Python の dict は、ランダム化された探査でオープンなアドレスを用いて、負荷因子が約 0.66 を超えるときにリハッシュします。 Java の HashMap では、リンクされたリストとの連鎖(Java 8+ で大きなバケットのバランスの取れた木に変換)を使用し、ロード因子 0.75 で再加速します。 C++ の unordered_map では、通常、チェーニングを使用しますが、実装は異なります。 データベースシステム ハッシュマップはまた、データベースのインデックスに広く使用されています。多くのデータベースシステムはハッシュインデックスを使用してデータの取得を加速します。これらのインデックスは、インデックスされた列をハッシュし、結果のキー値のカップルをハッシュテーブルに保存することで、迅速な検索を可能にします。 ハッシュインデックスを使用するいくつかの一般的なデータベースシステムには、以下が含まれます。 PostgreSQL: ハッシュ インデックスをサポートしますが、B ツリー インデックスほど一般的に使用されていません。 MongoDB: ハッシュ インデックスを使用して、ハッシュ フィールドにおけるシェアリングおよび平等 クエリをサポートします。 Redis:ハッシュマップをコアデータ構造として実装し、キー値のカップルの効率的なストレージと検索を可能にします。 これらの実装はしばしば、前述のハッシュと衝突解消の同じ基本原則を活用するが、データベースの文脈に特有の追加の最適化も含める可能性がある。 バージョン制御 Git のようなバージョン制御システムは、ハッシュマップを使用してファイルの変更を効率的に管理し、バージョンを追跡します。 Git の各コミットは、そのコンテンツの SHA-1 ハッシュによって識別され、これはコミットオブジェクトのユニークなキーとして機能します。これにより、Git はリポジトリ内のコミット、ブランド、その他のオブジェクトを迅速に検索することができます。 すべてを組み合わせる:知識の実装がどのように重要か そして、それは理論だけではありません! ハッシュマップがあなたの選択したプログラミング言語でどのように実装されているかを理解することは、あなたのコードのパフォーマンスの大幅な改善につながります。 たとえば、Pythonの 最適化された文字列処理でオープンなアドレッシングを使用し、これを理解すると、より良いパフォーマンスにつながることができます。 dict Bad Implementation (Fights Against Python's Dict) def count_words_bad(text): word_counts = {} words = text.split() for word in words: # This is inefficient with open addressing! if word in word_counts: # First lookup word_counts[word] += 1 # Second lookup + assignment else: word_counts[word] = 1 # Third lookup + assignment return word_counts 全画面モード 全画面モード 全画面モード 全画面モード Problems: 単語ごとに複数のハッシュ検索(最大3つまで!) Open addressing makes key-missing checks expensive. オープンアドレスは鍵が欠けているチェックを高価にします。 Pythonの dict optimizations を活用しない Good Implementation (Works With Python's Dict) from collections import defaultdict, Counter def count_words_good_v1(text): # defaultdict eliminates key existence checks word_counts = defaultdict(int) words = text.split() for word in words: word_counts[word] += 1 # Single operation! return dict(word_counts) def count_words_good_v2(text): # Counter is optimized specifically for Python's dict implementation words = text.split() return Counter(words) def count_words_good_v3(text): # dict.get() with default avoids the membership test word_counts = {} words = text.split() for word in words: word_counts[word] = word_counts.get(word, 0) + 1 # Single lookup return word_counts 全画面モード 全画面モード 全画面モード 全画面モード Why These Are Better: 単一ハッシュ単語ではなく複数のハッシュ単語 Python の string optimization を活用 - ストレージ キーは非常に効率的に処理されます。 オープンなアドレスで動作する - より少ない探査操作が必要 Counter のような内蔵の最適化を使用し、Python の実装に調節されています。 性能の違い 良い実装はしばしば2〜3倍速く、単にPythonのDictの実装を理解し、それに反対するよりも働くことによって! Typical Results: 結論 ハッシュマップは、コンピュータサイエンスで最も基本的で強力なデータ構造の1つであり、近代的なプログラミングにおいて不可欠なデータへのほぼ恒久的な時間アクセスを提供しています。 重要な洞察点は、ハッシュマップの「魔法」は全く魔法ではありません - それはよく設計されたアルゴリズムとデータ構造の結果です。これらの内部を理解すると、O(n)最悪のシナリオを回避し、より効率的なコードを書くことができます。 Key Takeaways: ハッシュ機能は基礎であり、データがどのように均等に配布され、衝突率に直接影響を与えるかを決定する。 衝突解消戦略にはそれぞれ異なる妥協点があります:シンプルさと強度のためのチェーニング、メモリ効率とキャッシュパフォーマンスのためのオープンなアドレス リハシングによるロードファクター管理は、ハッシュマップが成長するにつれてパフォーマンスの低下を防ぐ 実装の知識は実際のパフォーマンス向上に転換します - あなたの言語が連鎖またはオープンアドレスを使用するかどうかを理解すると、あなたのコードが2〜3倍速くなることができます。 Python スクリプトの最適化、Java でのパフォーマンス問題のデバッグ、またはデータベースシステムのアーキテクチャーの決定を行う場合、この HashMap インターナルの理解は、あなたが知的選択をするためのツールを提供します。 で、 あるいは キャップの下で何が起こっているか、そしてこれらの素晴らしいデータ構造を最大限に活用する方法を正確に知ることができます。 dict HashMap unordered_map ハッシュマップは本当に素晴らしい - そして今あなたは何故か知っています!