.NET中的字典是用来存储多组”键和值”的容器,其中键必须有值且不能重复,值没有任何要求。
但是很多时候我们需要一种保存类似”键和键”的字典,比如字典中的一组对应a-b,从a键可以得到值b,同时也可以从b键得到值a。字典中的两组数据既都是值,也是键,因此必须都有值且不能重复,我们把这组值称为键1和键2,他们都可以通过一面而找到另一面。
这样的话,添加操作需要确保键1和键2不能已经存在,编辑操作也需要注意被修改键值的新值的唯一性,比如这种字典中1的对应值是a,现在要想把1-a改成1-b,那么必须确保b在键2中不存在,因为如果继续将1-a被改成1-b,从另一面会出现两个键都是b的非法情况。
这个类的名称是KeyDictionary,它具有如下特点:
- 仍然采用泛型:KeyDictionary<TKey1, TKey2>,其中TKey1和TKey2分别代表键1和键2的类型
- 继承IEnumerable<KeyValuePair<TKey1, TKey2>>,很显然这种类型的字典不适合继承IDictionary或ICollection
- 继承IClonable,支持元素的浅层拷贝(如果元素是值类型,当然就是深层拷贝)
- 支持自定义IEquatableComparer<TKey1>和IEquatableComparer<TKey2>
- 虽然没有继承IDictionary,但是提供所有字典的典型操作函数,而且是基于两个键的
取值:GetValueFromKey和GetPairFromKey
分别有GetValueFromKey1或2,GetPairFromKey1或2 四个函数。参数是键1或键2。
GetValue返回对应的键值,如果没有的话抛出异常。
GetPair返回一组键,类型是Nullable<KeyValuePair>,如果没有的话返回null。
var kdic = new KeyDictionary<int, string>() { {12, "str 12"}, {9, "str 9"}, {-2, "负二"} }; Console.WriteLine(kdic.GetValueFromKey1(9)); //str 9 Console.WriteLine(kdic.GetValueFromKey2("str 12")); //12 Console.WriteLine(kdic.GetPairFromKey2("负二")); //[-2, 负二] (调用KeyValuePair的ToString) Console.WriteLine(kdic.GetPairFromKey1(-3) == null); //True (因为没有键-3,返回null)查询:ContainsKey和ContainsPair
分别有ContainsKey1或2,和ContainsPair 3个函数
ContainsKey不用解释了吧,返回字典中有没有键1或键2.
ContainsPair比较有意思,它是一个更高级的ContainsKey执行。它的参数是两个键(键1和键2),它返回一个Tuple<bool, bool, bool>,即3个布尔值。前两个表示字典里有没有键1和键2,第三个表示键1和键2是不是对应的一对键值,所以只有前两个都为true时,第三个才有可能是true。
var kdic = new KeyDictionary<int, string>() { {12, "str 12"}, {9, "str 9"}, {-2, "负二"} }; Console.WriteLine(kdic.ContainsKey1(-3)); //false Console.WriteLine(kdic.ContainsKey2("str 9")); //true Console.WriteLine(kdic.ContainsPair(12, "不存在的字符串")); //true false false Console.WriteLine(kdic.ContainsPair(-2, "负二")); //true true true添加和修改:Add和EditKey
分别对应Add,EditKey1或2 3个函数。
Add不需要多说,两个键都不能已存在,否则异常抛出。
EditKey在文章开头讲“编辑”的时候也提到过,必须保证新的键值也不能已存在,否则一场抛出。
var kdic = new KeyDictionary<int, string>(); kdic.Add(1, "one"); kdic.EditKey2("one", 11111); Console.WriteLine(kdic.GetValueFromKey1(11111)); //one删除:Remove和Clear
包括RemoveFromKey1或2,Clear 3个函数。
可以通过键1或键2来删除一组键值,或者Clear直接清空容器。
var kdic = new KeyDictionary<int, string>() { {1, "one"}, {2, "two"} }; kdic.RemoveFromKey2("two"); Console.WriteLine(kdic.Count); //1 kdic.Clear(); Console.WriteLine(kdic.Count); //0个数:容量和大小
在构造函数里可以指定容器的初始容量(Capacity)。同时可以通过Count来得到容器的实际大小。
(这个属于最基本的容器属性,就不用举代码示例了)
枚举:GetEnumerator和Keys
KeyDictionary继承IEnumerable<KeyValuePair<TKey1, Tkey2>>,因此用foreach可以枚举其键值元素。
IDictionary里有Keys和Values属性,很显然,对于KeyDictionary来说,两个对应的数据都是Key也从另一面都是Value,因此KeyDictionary用Keys1和Keys2来返回两组键的集合。
var kdic = new KeyDictionary<int, string>() { {1, "one"}, {2, "two"} }; foreach(KeyValuePair<int, string> pair in kdic) Console.WriteLine(pair); foreach(int key1 in kdic.Keys1) Console.WriteLine(key1); foreach(string key2 in kdic.Keys2) Console.WriteLine(key2); //输出: /* [1, one] [2, two] 1 2 one two */元素拷贝
你可以使用Clone方法(继承与IClonable)或者使用构造函数,都可以将一个KeyDictionary的内容拷贝到另一个KeyDictionary内。
var kdic = new KeyDictionary<int, string>() { {1, "one"}, {2, "two"} }; var kdic2 = new KeyDictionary<int, string>(kdic); //也可以使用kdic2 = (KeyDictionary<int, string>)kdic.Clone(); //两个是一样的 foreach(var pair in kdic2) Console.WriteLine(pair); //输出: //[1, one] //[2, two]自定义IEquatableComparer
就像普通字典构造函数的IEquatableComparer,只不过KeyDictionary用两个IEquatableComparer来代表两个键。
var kdic = new KeyDictionary<string, string>(StringComparer.OrdinalIgnoreCase, EqualityComparer<string>.Default); kdic.Add("abc", "abc"); Console.WriteLine(kdic.ContainsKey1("ABC")); //true Console.WriteLine(kdic.ContainsKey2("ABC")); //false源码下载: 点击下载