本文分析的是 中截止至 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 factor
比max_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...