libc hashtable 原始碼簡析

2021-07-09 18:51:19 字數 3295 閱讀 7893

本文分析的是 中截止至 2016 年 1 月 30 日最新的libc++

libc++中,hashtable的實現為鏈式結構。 在教科書(introduction to algorithm 3rd edition)中,介紹的實現是由乙個陣列作為buckets,每個陣列中儲存乙個鍊錶。但是libc++中,使用乙個單向鍊錶貫穿整個hashtable,每個slot儲存的是上乙個元素結點的指標,這個元素充當當前鍊錶的頭結點。也就是說,每個slot的鍊錶儲存的實際上是乙個左開右閉的區間。在libc++中,實現鍊錶普遍使用了乙個技巧:有乙個基類***_node_base,這個裡面僅儲存指向下乙個(或者還有上乙個)結點的指標,而真正的***_node裡面才有元素。選用***_node_base作為頭結點,這樣就減少了單個元素的記憶體占用。在hashtable中,可以找到:

template

struct __hash_node_base

};template

struct __hash_node

: public __hash_node_base

<

typename __rebind_pointer<_voidptr, __hash_node<_tp, _voidptr> >::type

>

;

以下是hashtable的關鍵成員變數:

typedef unique_ptr<__node_pointer, __bucket_list_deleter> __bucket_list;

// --- member data begin ---

__bucket_list __bucket_list_;

__compressed_pair<__first_node, __node_allocator> __p1_;

__compressed_pair__p2_;

__compressed_pair __p3_;

// --- member data end ---

可以看到:

1.buckets是乙個node_pointer的陣列;

2.__p1_.first()就是上文中說的沒有元素的頭結點;

3.__p2_.first()是現在的元素個數;

4.__p3_.first()是現在的load factor

在下文的**中為了便於理解,__p1_first()替換為head_node

看過rehash函式後,就會對整個hashtable有個大致的印象。其大致思路為:若當前結點(cp或許指的是current pointer?)與前乙個結點的雜湊值相同則不用理會——讓它跟著前乙個結點走好了。否則就說明我們找到了邊界,那麼就在這個hashtable的鍊錶最前面插入當前結點。這時候分兩種情況。第一種是那個slot是空的,那麼就把當前結點對應的新slot更新為整個鍊錶的頭結點,把原來以整個鍊錶的頭結點為區域性煉表頭結點的slot更新為這個結點的指標(這樣就形成了開區間),第二種是非空,那麼直接插入就好了。但是這裡有個小優化:從current_node開始,把所有相等元素的結點同時插入進去,由於是鍊錶,這個操作可以o(1)完成!另外,這個hashtable會把每個元素的hash value都快取下來,這樣在rehash的時候無需再次hash

template

void

__hash_table<_tp, _hash, _equal, _alloc>::__rehash(size_type __nbc)

else}}

}}

}

了解了rehash的思路,那麼insert也就十分明了了:如果插入後的新load factormax_load_factor大了,就rehash。(由於這個是insert_unique,所以一旦發現重複的就不管了) 如果待插入的slot是空,那麼把新結點插入到最前面,然後整個鍊錶的頭結點設定為這個slot的頭結點,然後將原先以head_node為頭結點的區域性鍊錶的頭結點更新為這個新插入的結點(這個新插入的結點在它們前面,實現了左開右閉),如果slot非空,就直接插入好了:

template

pair::iterator, bool>

__hash_table<_tp, _hash, _equal, _alloc>::__node_insert_unique(__node_pointer __nd)}}

// insert_after __bucket_list_[__chash], or __first_node if bucket is null

__node_pointer __pn = __bucket_list_[__chash];

if (__pn == nullptr)

else

__ndptr = __nd;

// increment size

++size();

__inserted = true;

}__done:

#if _libcpp_debug_level >= 2

return pairbool>(iterator(__ndptr, this), __inserted);

#else

return pairbool>(iterator(__ndptr), __inserted);

#endif

}

AbstractCollection原始碼分析

abstractcollection抽象類提供了collection的骨架實現,collection分析請看 這裡直接看它的 是如何實現的.public abstract iterator iterator 該方法沒有實現.public abstract int size 該方法沒有實現.publi...

ThreadPoolExecutor原始碼閱讀

執行緒池解決兩個問題 一是復用執行緒,減少建立銷毀執行緒帶來系統開銷 二是限定系統資源使用邊界,避免大量執行緒消耗盡系統記憶體 適用於互不依賴,執行時間短,不需要對執行緒控制操作的執行緒 新增任務時,1.若執行緒數量小於corepoolsize,則新增執行緒執行任務 2.若執行緒數量大於等於core...

OrangePi One Android 原始碼編譯

一 系統環境搭建參照 二 lichee原始碼編譯 1.檢視help build.sh h2.配置核心 cd linux 3.4 make arch arm menuconfig 進入配置頁面,上下移動列表,空格是選擇列表,左右移動選擇退出選項 3.首次編譯執行清除 在 lichee linux3.4...