You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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}");
}

方案优势

  1. 内存高效:每个条目只存储一次,两个索引字典仅保存条目引用,避免了重复存储值或键映射的冗余。
  2. 查找高效:字典的查找复杂度是O(1),查询时直接通过索引定位条目列表,性能拉满。
  3. 灵活性强:支持键不唯一的场景,同时如果需要回到“键唯一”的要求,只需在Add方法中添加校验(比如检查_key1Index.ContainsKey(key1),如果存在就抛出异常或返回错误)。

如果需要更复杂的场景(比如支持更多键、线程安全等),还可以基于这个思路扩展,比如把索引字典改成Dictionary<string, HashSet<Entry>>来避免重复条目,或者添加锁实现线程安全。

内容的提问来源于stack exchange,提问作者LLLL_easycomeeasygo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:18:35