.NET中如何创建支持单键访问的双键字典(键可非唯一)
实现.NET双键可访问的多条目字典
我完全理解你的需求:想要一个能通过两个键中任意一个快速查询的字典结构,而且现在还放宽了键的唯一性限制,允许同一个键对应多个条目,查询时返回匹配的完整条目列表。你之前用四个字典的方式虽然能跑,但确实有点冗余,咱们来看看更简洁高效的实现方案。
核心思路:自定义封装双索引字典
与其维护四个独立字典,不如只存储一份完整条目,然后为两个键分别建立索引字典,这样既节省内存,又能保证O(1)的查找效率。
第一步:定义条目结构
首先用record(C# 9+)定义存储每个条目的数据结构,自带值相等语义,用起来很方便:
public record Entry(string Key1, string Key2, string Value);
第二步:封装双键字典类
我们创建一个DualKeyDictionary类,内部维护两个索引字典,分别映射Key1和Key2到对应的条目列表:
public class DualKeyDictionary { // Key1到条目列表的索引,支持忽略大小写(可根据需求调整) private readonly Dictionary<string, List<Entry>> _key1Index = new(StringComparer.OrdinalIgnoreCase); // Key2到条目列表的索引 private readonly Dictionary<string, List<Entry>> _key2Index = new(StringComparer.OrdinalIgnoreCase); /// <summary> /// 添加一条双键条目 /// </summary> public void Add(string key1, string key2, string value) { var newEntry = new Entry(key1, key2, value); // 更新Key1索引 if (!_key1Index.TryGetValue(key1, out var key1Entries)) { key1Entries = new List<Entry>(); _key1Index[key1] = key1Entries; } key1Entries.Add(newEntry); // 更新Key2索引 if (!_key2Index.TryGetValue(key2, out var key2Entries)) { key2Entries = new List<Entry>(); _key2Index[key2] = key2Entries; } key2Entries.Add(newEntry); } /// <summary> /// 索引器:通过任意键(Key1或Key2)查询条目列表 /// </summary> public List<Entry> this[string key] { get { // 优先查Key1,再查Key2,都没有返回空列表 if (_key1Index.TryGetValue(key, out var entries)) { return entries.ToList(); // 返回副本,避免外部修改内部集合 } if (_key2Index.TryGetValue(key, out entries)) { return entries.ToList(); } return new List<Entry>(); } } /// <summary> /// 移除指定条目(可选实现) /// </summary> public bool Remove(string key1, string key2, string value) { // 先找到要移除的条目 var targetEntry = _key1Index.TryGetValue(key1, out var key1Entries) ? key1Entries.FirstOrDefault(e => e.Key2 == key2 && e.Value == value) : null; if (targetEntry == null) return false; // 从两个索引中移除 key1Entries.Remove(targetEntry); if (key1Entries.Count == 0) _key1Index.Remove(key1); var key2Entries = _key2Index[key2]; key2Entries.Remove(targetEntry); if (key2Entries.Count == 0) _key2Index.Remove(key2); return true; } }
使用示例
var userDict = new DualKeyDictionary(); // 添加条目,支持同一个Key1对应多个Key2 userDict.Add("alice", "alice@example.com", "Alice Smith"); userDict.Add("alice", "alice.work@example.com", "Alice Smith"); userDict.Add("bob", "bob@example.com", "Bob Johnson"); // 通过Key1查询 var aliceEntries = userDict["alice"]; foreach (var entry in aliceEntries) { Console.WriteLine($"用户名:{entry.Key1},邮箱:{entry.Key2},姓名:{entry.Value}"); } // 通过Key2查询 var bobEmailEntries = userDict["bob@example.com"]; foreach (var entry in bobEmailEntries) { Console.WriteLine($"用户名:{entry.Key1},邮箱:{entry.Key2},姓名:{entry.Value}"); }
方案优势
- 内存高效:每个条目只存储一次,两个索引字典仅保存条目引用,避免了重复存储值或键映射的冗余。
- 查找高效:字典的查找复杂度是O(1),查询时直接通过索引定位条目列表,性能拉满。
- 灵活性强:支持键不唯一的场景,同时如果需要回到“键唯一”的要求,只需在
Add方法中添加校验(比如检查_key1Index.ContainsKey(key1),如果存在就抛出异常或返回错误)。
如果需要更复杂的场景(比如支持更多键、线程安全等),还可以基于这个思路扩展,比如把索引字典改成Dictionary<string, HashSet<Entry>>来避免重复条目,或者添加锁实现线程安全。
内容的提问来源于stack exchange,提问作者LLLL_easycomeeasygo
相关产品推荐
相关产品推荐

