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

C#双键字典TryGetValue优化及非多键字典替代方案咨询

双键字典的高效实现优化方案

现有代码的核心问题

你当前的TryGetValue实现是线性遍历整个集合(时间复杂度O(n)),数据量增大后性能会急剧下滑,这是最需要解决的问题。另外参数逻辑存在模糊性——比如同时传入key1和key2时,是要匹配同时满足两个键的条目吗?以下方案基于「支持单独用key1/key2查找,或同时用两个键精确查找」的需求设计。

高效实现思路:双索引字典

不需要维护两个存储相同值的独立字典,而是用一个主字典存储完整的<(TKey1, TKey2), TValue>条目,同时维护两个索引字典分别映射单个键到对应的复合键集合。这样所有查找操作都能达到O(1)的时间复杂度,且不会重复存储值,兼顾性能和内存效率。

完整实现代码

public class DualKeyDictionary<TKey1, TKey2, TValue> : IEnumerable<KeyValuePair<(TKey1, TKey2), TValue>>
    where TKey1 : notnull
    where TKey2 : notnull
{
    // 主存储:保存完整双键-值对
    private readonly Dictionary<(TKey1, TKey2), TValue> _mainDict = new();
    // 索引1:Key1 -> 对应所有复合键条目
    private readonly Dictionary<TKey1, List<(TKey1, TKey2)>> _key1Index = new();
    // 索引2:Key2 -> 对应所有复合键条目
    private readonly Dictionary<TKey2, List<(TKey1, TKey2)>> _key2Index = new();

    public void Add(TKey1 key1, TKey2 key2, TValue value)
    {
        var compositeKey = (key1, key2);
        _mainDict.Add(compositeKey, value);

        // 更新Key1索引
        if (!_key1Index.TryGetValue(key1, out var key1Entries))
        {
            key1Entries = new List<(TKey1, TKey2)>();
            _key1Index[key1] = key1Entries;
        }
        key1Entries.Add(compositeKey);

        // 更新Key2索引
        if (!_key2Index.TryGetValue(key2, out var key2Entries))
        {
            key2Entries = new List<(TKey1, TKey2)>();
            _key2Index[key2] = key2Entries;
        }
        key2Entries.Add(compositeKey);
    }

    public bool TryGetValue(out TValue value, TKey1 key1 = default, TKey2 key2 = default)
    {
        value = default;
        bool hasKey1 = !EqualityComparer<TKey1>.Default.Equals(key1, default);
        bool hasKey2 = !EqualityComparer<TKey2>.Default.Equals(key2, default);

        // 未传入任何键,直接返回false
        if (!hasKey1 && !hasKey2)
            return false;

        // 同时传入两个键:直接查主字典做精确匹配
        if (hasKey1 && hasKey2)
        {
            return _mainDict.TryGetValue((key1, key2), out value);
        }

        // 仅传入Key1:通过索引快速定位,返回第一个匹配值
        if (hasKey1)
        {
            if (_key1Index.TryGetValue(key1, out var entries) && entries.Count > 0)
            {
                value = _mainDict[entries[0]];
                return true;
            }
            return false;
        }

        // 仅传入Key2:通过索引快速定位,返回第一个匹配值
        if (_key2Index.TryGetValue(key2, out var entries) && entries.Count > 0)
        {
            value = _mainDict[entries[0]];
            return true;
        }
        return false;
    }

    // 实现遍历接口(可选)
    public IEnumerator<KeyValuePair<(TKey1, TKey2), TValue>> GetEnumerator()
    {
        return _mainDict.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

关键优化点

  • 性能提升:所有查找操作均为O(1)级别,彻底解决线性遍历的性能瓶颈
  • 内存高效:索引仅存储复合键的引用/副本,不会重复存储TValue,比维护两个独立字典更节省内存
  • 逻辑清晰:明确区分三种传参场景,避免模糊匹配

简化替代方案(仅精确双键匹配)

如果你的需求只是通过完整的双键组合查找,不需要单独用单个键查找,直接用.NET内置的Dictionary<(TKey1, TKey2), TValue>即可,这是最轻量化的方案。

额外说明

  • 如果需要支持同一个key1/key2对应多个值的场景,可以修改TryGetValue返回IEnumerable<TValue>,或新增GetValuesByKey1(TKey1 key1)这类方法
  • 代码中添加了notnull泛型约束,如果需要支持null键,可以去掉约束并在代码中补充null值判断逻辑

内容的提问来源于stack exchange,提问作者Enrique.go

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 09:19:54