1. 緣起:
假設(shè)我們的用戶管理系統(tǒng)要求用戶的 ID 和 Name 都必須是唯一的,并且用戶的 ID 和 Name 一經(jīng)確定就不能被修改。而且管理系統(tǒng)經(jīng)常需要根據(jù) ID 來(lái)查找 Name ,也經(jīng)常需要根據(jù) Name 來(lái)查找 ID 。根據(jù)這樣的需求,我們可以考慮使用一個(gè) Dictionary 來(lái)將 ID 和 Name 緩存起來(lái),通常 ID 作為 Key , Name 作為 Value 。這樣便可實(shí)現(xiàn)通過(guò) ID 查詢 Name 的快速查找,但是,通過(guò) Name 查找 ID 就不是那么快了,因?yàn)樯婕暗綄?duì) Dictionary 的 Values 做遍歷的操作。那么,有可能使得通過(guò) Name 查找 ID 的速度與通過(guò) ID 查找 Name 的速度一樣快嗎?
于是,我設(shè)計(jì)了 ESBasic.ObjectManagement.Cache.IBidirectionalMapping (雙向映射)來(lái)解決這個(gè)問(wèn)題。
雙向映射的形象示意圖如下:
2. 適用場(chǎng)合:
如果滿足以下的條件,則可以使用雙向映射:
(1) Key 是唯一的, Value 也是唯一的。
(2) 需要對(duì) Key 和 Value 做緩存。
(3) 經(jīng)常需要根據(jù) Key 來(lái)查找 Value 。
(4) 經(jīng)常需要根據(jù) Value 來(lái)查找 Key 。
3 .設(shè)計(jì)思想與實(shí)現(xiàn)
IBidirectionalMapping 接口定義如下:
/// IBidirectionalMapping雙向映射。即Key和Value都是唯一的,在這種情況下使用IBidirectionalMapping可提升依據(jù)Value查找Key的速度。
/// 該接口的實(shí)現(xiàn)必須是線程安全的。2008.08.20
/// </summary>
public interface IBidirectionalMapping < T1,T2 >
{
int Count{ get ;}
/// <summary>
/// Add添加映射對(duì)。如果已經(jīng)有相同的key/value存在,則會(huì)覆蓋。
/// </summary>
void Add(T1t1,T2t2);
void RemoveByT1(T1t1);
void RemoveByT2(T2t2);
T1GetT1(T2t2);
T2GetT2(T1t1);
bool ContainsT1(T1t1);
bool ContainsT2(T2t2);
/// <summary>
/// GetAllT1ListCopy返回T1類型元素列表的拷貝。
/// </summary>
IList < T1 > GetAllT1ListCopy();
/// <summary>
/// GetAllT2ListCopy返回T2類型元素列表的拷貝。
/// </summary>
IList < T2 > GetAllT2ListCopy();
}
該接口使用了兩個(gè)泛型參數(shù),根據(jù)上面的描述,一個(gè)泛型參數(shù)表示 Key 的類型,另一個(gè)泛型參數(shù)表示 Vlaue 的類型。由于,在雙向映射中, Key 和 Value 是對(duì)稱的,所以我沒(méi)有使用 TKey 和 TValue 來(lái)命名它們,而是使用 T1 和 T2 。
在實(shí)現(xiàn) BidirectionalMapping 時(shí),我們使用兩個(gè) Dictionary 來(lái)完成雙向映射的功能。一個(gè) Dictionary 以 T1 為 Key , T2 為 Value ;另一個(gè)剛好反過(guò)來(lái)。
在實(shí)現(xiàn)的具體過(guò)程中,要注意以下幾點(diǎn):
(1) 為了允許在多線程的環(huán)境中使用雙向映射,所以 BidirectionalMapping 必須在對(duì)內(nèi)部 Dictionary 操作的時(shí)候進(jìn)行加鎖控制。
(2) 在實(shí)現(xiàn) Add 方法添加一個(gè)“映射對(duì)”的時(shí)候,必須判斷當(dāng)前是否已經(jīng)存在了相同的值,如果存在,則先刪除舊的映射對(duì),再添加新的映射對(duì)。
(3) 要注意一個(gè)細(xì)節(jié), GetAllT1ListCopy 和 GetAllT2ListCopy 的實(shí)現(xiàn)都使用了 lock ,這是因?yàn)樵诳截惖臅r(shí)候會(huì)對(duì)其 Keys 或 Values 進(jìn)行 foreach 遍歷,而在對(duì) Dictionary 中的元素進(jìn)行 foreach 遍歷的時(shí)候,如果同時(shí)向其中添加或刪除元素,則 foreach 操作是會(huì)拋出異常的。
4. 使用時(shí)的注意事項(xiàng)
BidirectionalMapping 提升了通過(guò) Name 查找 ID 的速度,這是通過(guò)使用了更大的內(nèi)存來(lái)做到的,是典型的“空間換時(shí)間”的例子。所以,對(duì)于巨大規(guī)模的映射對(duì)的緩存,要注意內(nèi)存的使用問(wèn)題。
另外,映射對(duì)中的兩個(gè)元素的類型不一定非是 ID 或 Name 這樣的簡(jiǎn)單對(duì)象,實(shí)際上,非常復(fù)雜的對(duì)象也可以緩存在雙向映射中,只要其 GetHashCode 方法實(shí)現(xiàn)的恰當(dāng)就不會(huì)有任何問(wèn)題。
5. 擴(kuò)展
雙向映射 BidirectionalMapping 暫時(shí)沒(méi)有任何擴(kuò)展。注:ESBasic源碼可到 http://esbasic.codeplex.com/ 下載。
ESBasic討論:37677395
ESBasic開(kāi)源前言
ESBasic 可復(fù)用的.NET類庫(kù)(11) -- 雙向映射 IBidirectionalMapping
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
