【问题标题】:Data structure for complex keys in dictionary字典中复杂键的数据结构
【发布时间】:2017-04-06 15:59:45
【问题描述】:

如果字典的键很复杂(值的组合) 例如,键可以是一对字符串和整数。

您建议使用什么数据类型作为密钥?如果从字典中进行大量读取,我会考虑性能和内存使用情况,分配繁重的数据类型可能不适合该任务。

我测试了 4 种不同的策略。

  1. 最幼稚的一种,使用字符串作为键并连接组件:

    int Read(string str, int num)
    {
        var key = str + "|" + num;
        return dict[key];
    }
    
  2. 使用元组表示键:

    int Read(string str, int num)
    {
        var key = new Tuple<string, int>(str, num);
        return dict[key];
    }
    
  3. 使用 KeyValuePair 表示键:

    int Read(string str, int num)
    {
        var key = new KeyValuePair<string, int>(str, num);
        return dict[key];
    }
    

我不太喜欢第一种方法,带有元组的方法看起来更优雅。 然而,元组与字符串并没有太大区别,因为它们都是类并且分配它们可能很昂贵。 KeyValuePair 似乎是最可行的数据类型,但在运行了一些测试后,我发现它的性能比字符串或元组差得多,鉴于 KeyValuePair 没有实现 GetHashCode(),这对我来说现在似乎很明显。 然后我尝试实现我自己的覆盖 Equals 和 GetHashCode() 的“KeyValuePair”:

struct KVPair<K, V>
{
    public K Key { get; set; }

    public V Value { get; set; }

    public KVPair(K key, V value)
    {
        Key = key;
        Value = value;
    }

    public override bool Equals(object obj)
    {
        if (!(obj is KVPair<K,V>))
        {
            return false;
        }

        KVPair<K, V> other = (KVPair<K, V>)obj;
        return Key.Equals(other.Key) &&
            Value.Equals(other.Value);
    }

    public override int GetHashCode()
    {
        int keyHash = Key.GetHashCode();
        int valHash = Value.GetHashCode();

        return (((keyHash << 5) + keyHash) ^ valHash);
    }
}

并将其用作我字典中的键:

    int Read(string str, int num)
    {
        var key = new KVPair<string, int>(str, num);
        return dict[key];
    }

它似乎比字符串和元组选项表现得更好,比原生的 KeyValuePair 好得多。

我只是想听听你会推荐什么。 在必须实现自己的数据类型时,我总是很谨慎,因为 FCL 通常会处理这个问题。

【问题讨论】:

    标签: c# .net dictionary tuples


    【解决方案1】:

    我更喜欢使用带有相关属性名称的特定类型:

    public class RowKey
    {
        public string Title { get; set; }
        public int Id { get; set; }
    
        public RowKey()
        {
        }
    
        public RowKey(string title, int id)
        {
            Title = title;
            Id = id;
        }
    
        public override bool Equals(object obj)
        {
            if (!(obj is RowKey))
                return false;
    
            RowKey other = obj as RowKey;
            return Title.Equals(other.Title) && Id.Equals(other.Id);
        }
        public override int GetHashCode()
        {
            int titleHash = Title.GetHashCode();
            int idHash = Id.GetHashCode();
    
            return (((titleHash << 5) + titleHash) ^ idHash);
        }
    }
    

    阅读使用:

    int Read(string str, int num)
    {
        var key = new RowKey(str, num);
        return dict[key];
    }
    

    【讨论】:

    • 但是这和使用元组不一样吗?这是一个类,我反对它的考虑是分配。
    • 是的,但是,请考虑一下您的代码结构和组织。元组是通用的,您可能会丢失上下文...
    • 在这种情况下反对使用元组是一个很好的观点。但是让你的键成为结构而不是类呢?
    • 对于您的用例,类可以是最快的,请参阅:dotnetperls.com/struct-versus-class
    猜你喜欢
    • 2015-11-08
    • 1970-01-01
    • 2012-11-11
    • 2014-09-29
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    相关资源
    最近更新 更多