HashMapのからは、Redisの辞書には、[ハッシュ]を参照してください。。。

序文

今日では、荒れた海での釣りを見てHashMap、ソースコード、学生がインタビューを頼む偉大な神の経験豊富なインタビュアーを考えるRedis 字典HashMap、ハッシュプロセスの任意異なります。正直なところ、また設計し、Redisのの実装(本当にお勧めします)読みますが、まさにそれを説明していないので、この記事を書きます。

注意:ハッシュプロセスでこの記事強調、ソースコードの一部が物語にそれを明快偉大な神を見るために〜

ハッシュ関数(ハッシュアルゴリズム)

  • ハッシュ関数ハッシュアルゴリズムとして知られているが、ハッシュ関数は、データの任意の種類から小さなデジタル「指紋」を作成する方法であり、この「フィンガープリント」であり、ハッシュ値

  • アプリケーションのハッシュ関数は、ハッシュテーブルなどの情報の実際の転送を、確実にするために、そのようなデータ保護機能として、非常に広いです。本論文では、アプリケーションがハッシュテーブルの上にある説明します。

  • もちろん、私たちは、ハッシュ関数は、各キーがある「フィンガープリント」に対応することを保証していることを願っていたハッシュ値、いわゆるユニバーサルハッシュが、原因パフォーマンス、アプリケーションシナリオおよびその他の考慮事項に、あまりにも多く受け入れることができないハッシュ衝突を

  • ハッシュ衝突**:**ハッシュ関数の入力と出力は、ハッシュ関数の入力Aのハッシュ値としてユニークな対応関係ではない、Bは、Cが得られます

  • 共通のハッシュ関数:

直接アドレス指定可能なデジタル解析途中二乗法の方法私は折り畳まれたまま乱数加算方法

  • 確かに我々はすべてのHashMap、Redisの辞書クラスのシーンは、に基づいて選択されます知っているI滞在ハッシュ関数として、最適化されました。喜ばせるために、ここでは各方法の概念を繰り返すことはしません、ここで見てみましょう

ハッシュ衝突(ハッシュ衝突)ソリューション

  • hash?->散列算法的选择->散列冲突怎么解决おそらく、これは何のほとんどの同僚の考え方です。だから、私たちはどのような主要な競合を解決するために、ハッシュアルゴリズムを見て?

オープンアドレス指定可能

  • 競合すると、次の空のハッシュアドレスを探して、同社は最も簡単なアルゴリズムを以下の行ってきました。F(キー)=(F(キー)+ D)MOD M(D = 1,2、...、M-1)設定栗、のためにメートル12、26、37を順次挿入されている、(26 + 1)= 12%(37 + 1)12%は、ハッシュ衝突がハッシング、(37 + 2)= 12、4%、したがって再び、発生します値が同じではありません、競合が解決されます。

再ハッシュ

  • 第1のハッシュ関数の競合がスペアハッシュ関数演算を持つことができる場合、ハッシュ関数を複数用意しています。

チェーンアドレス方式

  • 加えて、ハッシュ衝突、私はハッシュ値滞在与えるために使用するリンクリストのノードは、1つの衝突ずつ挿入された場合、以下に示すように、構造体が形成されます。以下は一例であり、F(X)= MODキー12シーンは、リンクされたリストを形成し、48%12%0 12 = 12を見ることができます。

公法のオーバーフロー領域

  • 追加のパブリック記憶素子の使用は、ハッシュを格納するスペースの競合を値。

HashMapの

HashMapのハッシュアルゴリズム

エピソードLOAD_FACTOR(負荷率)

  • 我々は、すべてのことを知っているHashMapデフォルトがLOAD_FACTORあり0.75、それはどのようなそれがないですか、?痕跡を検索するために彼らの元に続く〜

  • newHashMapソースコードの注釈は教えてくれるcapacity、16のload_factor0.75。

/**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
复制代码
  • ではput要素、我々が見る、拡張を実行しなければならないresize()機能を、象徴的な掲示の部分をいいます。
else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
复制代码
  • 我々はによって、しきい値を見ることができcapacity掛けるload_factorことの結果を16 * 0.75 = 12HashMap拡張要素は、閾値以上のようことが理解されるであろう小容量に対する閾値、同じハッシュ関数で16個の要素にハッシュ衝突の確率があるため、大型のハッシュ衝突の確率を減少させますハッシュ衝突確率のチャンスは12個の要素よりも小さくなっています

ハッシュ関数と解析式

  • 他に類似のHashMapハッシュアルゴリズムIはなくによって、滞在MOD計算が、ビット操作と、仮定しキーた入力値を、ハッシュ関数は、F(キー)次のように具体的な式であります:
f(key) = hash(key) & (table.length - 1) 
hash(key) = (h = key.hashCode()) ^ (h >>> 16)
复制代码
  • hash(key) & (table.length - 1)table.length、実際には、ほとんどの役割を引き継ぐために最適化されたバージョンでは、私が取り外さ滞在に基づいています。HashMap各拡張の特性table.lengthとなり2 ^ n個、これより上位のビットの演算効率。
  • >>>符号なし右シフト演算は、我々はことを知っているhashCode()範囲が非常に広く、紛争自体の可能性は非常に小さいですが、とtable.length - 1確率は、大きくなるためtable.length、より小さい値。だからこその使用>>> 16理由、hashCode()のためのハイとローの両方のf(key)一定の影響力、より均一な分布、小さな上のハッシュ衝突の確率。

HashMapのハッシュ紛争解決

  • HashMap明白な解決策は、衝突のハッシュにあるチェーンアドレス法を実際には、構造から見ることができ、。
  • 処理も見ることができ、ソース以下のセクションを参照してください。上のコメント。HashMapresize()
if (oldTab != null) {
			// 遍历旧数组上的节点
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    // 当前链表只有一个节点(没有散列冲突的情况)
                    if (e.next == null)
	                    // 通过散列算法计算存放位置并放入
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
	                    // 红黑树去了。。。
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
	                    // 低位链表、高位链表
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
	                        // 遍历发生散列冲突的链表
                            next = e.next;
                            // hash值小于旧数组容量 放入低位链表
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            // hash值大于等于旧数组大小 放入高位链表
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        // 低位链表放在原来index下
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        // 高位链表放在原来index + 旧数组大小
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
复制代码

Redisの辞書

データ構造の概要

そして、HashMap同じ場所について

まず、Redisのデータ構造の簡単な辞書を簡単に説明する、私の大学時代は影のキャストされているC言語を。

typedef struct dictht {
	// 哈希表数组
	dictEntry **table;

	// 哈希表大小
	unsigned long size;

	// 哈希表大小掩码,用于计算索引值
	// 总是等于size - 1
	unsigned long sizemask;
	
	// 哈希表已使用节点数
	unsigned long used;
	
} dictht
复制代码
  • ハッシュテーブルの配列があり、それは少し慣れていないし、Node<K,V>[]同じ目的は、その後を見てdictEntry、このクラス。
typedef struct dictEntry {
	// 键
	void *key

	// 值
	union {
		void *val;
		unit64_tu64;
		int64_ts64;
	} v

	// next指针
	struct dictEntry *next;
}
复制代码
  • そしておなじみ少し、キーと値のペア!key属性は、キーの鍵を保持し、v属性は値で格納され、前記キーと値のペアは、ポインタであってもよいunit64_tint64_t整数。nextハッシュテーブルは、別のノードへのポインタであり、ポインタがリストの複数に接続された同一のハッシュキー値テーブルであってもよいです。

  • 私たちはこれまでのところ、見ることができる、HashMap基本的にはいくつかのより多くの追加属性(ハッシュテーブルのサイズ、ノードの数が使用されている、マスクなど)に加えて、区別を欠いています。

著名な場所

  • ただ、読んで基礎となる辞書は、次に辞書はそれが何であるかの真の顔である、ハッシュテーブル構造によって実装されていますか?
typedef struct dict {
	// 类型特定函数
	dictType *type;
	
	// 私有数据
	void *privdata;

	// 哈希表(上文讲的)
	dictht ht[2];

	// rehash索引
	// 当rehash不在进行时,值为-1
	int trehashidx;
}
复制代码
  • したがって、Redisのデータ構造は、辞書上のグラフの通常状態での辞書を導入しました。

Redisの辞書のハッシュアルゴリズム

  • 計算は、ハッシュ値が辞書のハッシュ値のハッシュ値を算出する機能プロセスに基づいて計算されます。hash = dict -> type->hashFunction(key)
  • sizemask財産とハッシュテーブルのハッシュ値を使用して、インデックス値は、状況に応じて、計算されますht[0]ht[1]そして、実際には同じ、コメントが言っているので、常に同じですHashMaphash(key) & (table.length - 1)sizemasksize - 1

index = hash & dict->ht[x].sizemask

  • Redisのハッシュアルゴリズムが使用されているMurmurHash2著者は、説明を買う余裕はない、特定の接続を。

辞書Redisのハッシュ紛争解決

  • Redisのハッシュテーブルも使用チェーンアドレス方式を、各ノードが有するnextポインタ、一方向のリンクされたリストを形成する複数のノードを、とHashMapの差が使用テール・ポインタ・テーブルに起因しない第1の補間をリストヘッダに新しいノードを追加します場所。

RedisのとHashMapの違い

子どもたちがやる違い、その後、大きな違いはありません、ハッシュアルゴリズム、ハッシュ競合解決モードを参照してください?つまり、再びハッシュ

二番煎じ

  • ただ、辞書データ構造、について話しました、2つのハッシュテーブル(があるht[2])、ここに秘密が。

  • Rehashキーが多すぎても少なすぎてものために、する必要がある場合には、ハッシュテーブルの負荷係数は、ハッシュテーブル、合理的な範囲内に維持されることを可能することを目指して伸縮します

  • 次のステップ:

    1. 辞書ht[1]行う場合、空間を割り当て、ハッシュテーブル拡張動作を、ht[1]第一以上の大きさHT [0] * 2 .used2 ^ n個、あなたが実行している場合収縮動作、ht[1]以上のサイズht[0].usedにします2 ^ n個
    2. 保存されるht[0]すべてのキーの値rehashht[1]、キーと値のペアが中に配置され、上記の、すなわち再計算されたハッシュ値とインデックスキー値ht[1]ハッシュ・テーブルに指定された場所。
    3. ときにht[0]すべてのキーをに移行するために含まれているht[1](後にht[0]空のハッシュテーブルとなる)、解放セットを、そしてにおける次のために新たに作成された空のハッシュテーブル調製。ht[0]ht[1]ht[0]ht[1]rehash
  • :プログレッシブ焼き直し記事は言いませんでした、私は本当にパワーを描いていなかった、データ構造が複雑で、ご了承ください。

概要

ハッシュアルゴリズム、ハッシュからこの記事の衝突解決の簡単な分析HashMap、景色行う必ずしもすべての権利、してください神の批判大!Redis 字典hash

リファレンス

おすすめ

転載: juejin.im/post/5d67c96c6fb9a06b160f4017