ESBasic 可复用的.NET类库(11) -- 双向映射

系统 1420 0

1. 缘起:

假设我们的用户管理系统要求用户的 ID Name 都必须是唯一的,并且用户的 ID Name 一经确定就不能被修改。而且管理系统经常需要根据 ID 来查找 Name ,也经常需要根据 Name 来查找 ID 。根据这样的需求,我们可以考虑使用一个 Dictionary 来将 ID Name 缓存起来,通常 ID 作为 Key Name 作为 Value 。这样便可实现通过 ID 查询 Name 的快速查找,但是,通过 Name 查找 ID 就不是那么快了,因为涉及到对 Dictionary Values 做遍历的操作。那么,有可能使得通过 Name 查找 ID 的速度与通过 ID 查找 Name 的速度一样快吗?

于是,我设计了 ESBasic.ObjectManagement.Cache.IBidirectionalMapping (双向映射)来解决这个问题。

双向映射的形象示意图如下:
ESBasic 可复用的.NET类库(11) -- 双向映射 IBidirectionalMapping


2. 适用场合:

如果满足以下的条件,则可以使用双向映射:

(1) Key 是唯一的, Value 也是唯一的。

(2) 需要对 Key Value 做缓存。

(3) 经常需要根据 Key 来查找 Value

(4) 经常需要根据 Value 来查找 Key

3 .设计思想与实现

IBidirectionalMapping 接口定义如下:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> /// <summary>
/// IBidirectionalMapping双向映射。即Key和Value都是唯一的,在这种情况下使用IBidirectionalMapping可提升依据Value查找Key的速度。
/// 该接口的实现必须是线程安全的。2008.08.20
/// </summary>
public interface IBidirectionalMapping < T1,T2 >
{
int Count{ get ;}

/// <summary>
/// Add添加映射对。如果已经有相同的key/value存在,则会覆盖。
/// </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();
}

该接口使用了两个泛型参数,根据上面的描述,一个泛型参数表示 Key 的类型,另一个泛型参数表示 Vlaue 的类型。由于,在双向映射中, Key Value 是对称的,所以我没有使用 TKey TValue 来命名它们,而是使用 T1 T2

在实现 BidirectionalMapping 时,我们使用两个 Dictionary 来完成双向映射的功能。一个 Dictionary T1 Key T2 Value ;另一个刚好反过来。

在实现的具体过程中,要注意以下几点:

(1) 为了允许在多线程的环境中使用双向映射,所以 BidirectionalMapping 必须在对内部 Dictionary 操作的时候进行加锁控制。

(2) 在实现 Add 方法添加一个“映射对”的时候,必须判断当前是否已经存在了相同的值,如果存在,则先删除旧的映射对,再添加新的映射对。

(3) 要注意一个细节, GetAllT1ListCopy GetAllT2ListCopy 的实现都使用了 lock ,这是因为在拷贝的时候会对其 Keys Values 进行 foreach 遍历,而在对 Dictionary 中的元素进行 foreach 遍历的时候,如果同时向其中添加或删除元素,则 foreach 操作是会抛出异常的。

4. 使用时的注意事项

BidirectionalMapping 提升了通过 Name 查找 ID 的速度,这是通过使用了更大的内存来做到的,是典型的“空间换时间”的例子。所以,对于巨大规模的映射对的缓存,要注意内存的使用问题。

另外,映射对中的两个元素的类型不一定非是 ID Name 这样的简单对象,实际上,非常复杂的对象也可以缓存在双向映射中,只要其 GetHashCode 方法实现的恰当就不会有任何问题。

5. 扩展

双向映射 BidirectionalMapping 暂时没有任何扩展。

注:ESBasic源码可到 http://esbasic.codeplex.com/ 下载。
ESBasic讨论:37677395
ESBasic开源前言

ESBasic 可复用的.NET类库(11) -- 双向映射 IBidirectionalMapping


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论