以前的
這篇文章
介紹了嵌入的變長資料結構(
embeded)
本文介紹乙個使用這種思想實現的通用
strmap
容器,相當於:
實現上使用了我以前寫的線索紅黑樹
——相比標準
map的實現,節省了一半的結點儲存開銷,而平均查詢時間只付出很小的額外開銷,並且沒有**膨脹問題。
是可選的,
allocator
總在類範圍,可以修改:
為了節省空間,
trbstrmap
使用乙個位元組儲存
strkey_length
,因此,
strlen(strkey)
不可超過
255,允許長度為0的
key。最大長度
255基本上可以應對絕大多數情況。 把
key作為變長字段,有乙個原因很重要:
key一旦插入,就不可改變!因此,整個結點的
memblock
也就不需要重新分配,可以修改的只有。
容器的iterator只有在刪除該node以後才失效」的語義!
一旦用在多執行緒環境中,問題就會變得很複雜,因為修改了父節點。就不能僅lock需要修改資料的那個結點,而是需要lock整個tree,使用資料庫的術語,就是一下子從記錄鎖變成了表鎖,本來按key查詢時,因為key沒改變,就不需要加鎖,而這樣搞,查詢時就需要對整個表加鎖,嚴重影響併發性!
注意事項:
1.
trbstrmap::iterator::value_type
和trbstrmap::value_type都是,
*iter
也是;而不是
std::map
的pair。
2.
trbstrmap::key(iter)
獲得iter
對應的key
3.
trbstrmap::klen(iter)
獲得key_length
,當然,
strlen(trbstrmap::key(iter))
也可以獲得
key_length
,但需要一些時間開銷;另外,當
key_content
中存在\0
時,klen
就是不可缺少的了(後面將詳細介紹這種情況)
4.
再次強調,
key_cstr_length
最大長度不可超過
255,但這個長度不包括末尾的\0
示例**:
typedef febird::trbstrmapsmap_t;
smap_t smap, smap2;
smap["1"] = 0;
smap["2"] = 1;
smap["3"] = 1;
smap["4"] = 2;
smap["1"]++;
smap["4"] += 2;
smap.insert("5", 5);
assert(!smap.insert("5", 6).second);
// str長度不可超過255
smap["aabbccddeeffgghhiijjkk***********************************=="] = 10;
for (typename smt::iterator i = smap.begin(); i != smap.end(); ++i)
在簡單的情況下,使用
trbstrmap,
比std::map
可以節省
50%~70%
的記憶體。使用的方便程度,基本上沒差別。
是模板引數,可以是任意struct/class
,當然也可以內嵌指標,也可以是複雜物件,沒有任何限制。
trbstrmap
允許加入內容中存在
\0的字串,這種應用比較罕見,但是仍然允許,只是必須遵循
以下注意事項:
1.
在這種情況下,需要傳入
key_content_length
引數:trbstrmap.probe(key,key_content_length)
trbstrmap.insert(key,key_content_length, val)
key_content_length
是整個key_content
長度,包括末尾的
\0——如果有的話
2.
或者,傳入
std::string
:trbstrmap[std::string(…)] = val;
equal to: trbstrmap.probe(s.c_str(), s.size()+1) = val;
3.
trbstrmap.insert(key, klen, val)
4.
這種情況下,應用一般需要傳入乙個自定義的
compare
5.
trbstrmap::klen(iter)
在任何情況下,返回的都是key_content_length – 1,
而不管末尾有沒有\0
6.
自定義函式需要獲取key_content_length時,應該如下:
inttrb_compare_custom ( const
struct trb_vtab* trb_restrict vtab,
const
struct trb_tree* trb_restrict tree,
const
void* trb_restrict x,
const
void* trb_restrict y)
7.
當給trbstrmap::find/lower_bound/equal_range
多傳入乙個
key_content_length
時,這些函式會將
key和
key_content_length
打包(這些都在庫內部完成),應用須知這會有稍微多一些的開銷。
必須這麼做,因為
compare
函式需要找到
key_content_length
,除此之外,別無他法。
8.
貌似很複雜,但始終堅持剃刀原理:在絕大多數情況下,也就是字串是
cstr
,末尾包含
\0時,函式的行為符合傳統慣例並且高效;在罕見情況下也可用,只是效率有一點下降,使用難度也大一些。
9.
當情況複雜時,記住:只要你明確傳入了長度,這個長度就是你真實資料的長度
**:
測試**:
資料結構 變長陣列
剛發現,以前用vector覺得挺簡單的,自己實現一下才知道這麼麻煩。真是佩服那些c 大師,寫出這麼好的東西。這算是乙個簡易的vector吧,之所以說它簡易,並不是因為功能少,而是實現的複雜度遠遠不及std vector 但基本原理是一樣的 1 pragma once23 const intinit ...
資料結構 堆疊的範例程式
堆疊本身可以使用靜態陣列結構或動態鍊錶結構實現,只要維持堆疊後進先出和從頂端讀取資料兩個基本原則即可。利用資料結構來實現堆疊的好處是演算法設計簡單,下面將用佇列來模擬堆疊。範例是 以陣列模擬撲克牌的洗牌及發牌過程,以點數取得撲克牌後放入堆疊,放滿52張牌後利用堆疊功能來給4個人發牌。include ...
資料結構與演算法 C 實現動態變長陣列
乙個簡單至極的變長陣列,僅僅是在原生陣列中新增了自動變長功能 參考stl中vector的實現,每次下標越界,就將陣列容量擴大一倍。申請新的空間,是原長度的二倍 從原記憶體複製所有內容到新記憶體 釋放原記憶體 author trialley date 2019 7 26 licence mit pra...