目录

介绍

概念化这个混乱

编码此混乱

警告

使用其他容器


有时有必要将集合用作字典键,但是这可能会导致性能下降并且不安全。以下是使它更快更安全的方法。

  • 下载源4.9 KB

介绍

使用C#进行功能样式编程时,您最终可能会遇到需要按项目的有序列表来键入字典的情况。有好的方法,但不是非常好的方法。本文旨在为您提供一种将列表用作字典键的有效且相对安全的方法。

概念化这个混乱

字典键必须具有可比性,以确保相等性,必须提供适当的哈希码,并且必须不可变。这些要求使使用列表作为字典键有点麻烦,因为列表通常不提供值语义——在这种情况下,是逐项比较和适当的哈希码。也许更糟的是,列表允许您随时修改它们。

总之,这里有两个主要问题,一个是逐项比较列表不是很有效,并且使用标准IList<T>实现作为字典键从根本上是不安全的,因为可以随时对其进行修改。

我们可以通过要求使用IReadOnlyList<T>来或多或少地解决后者,后者提供类似列表的访问,而不需要修改列表的方法。不过,当我们声明KeyList<T>类时,我们将在其中添加一个Add()方法。这样做的原因是允许您首先填写列表。如果我们想更加安全,则可以取消该Add()方法,并强制您将这些项作为数组传递给KeyList<T>的构造函数。未执行此操作的原因是为了提高性能。KeyList<T>的主要目的是性能,将数据传递给构造函数将需要一个副本。副本不一定很慢,但我们不需要它——我们在这里没事,因为任何采用IReadOnlyList<T>的操作都不会看到Add() 方法,这样,我们就避免了虚假的副本。

前者需要做更多的工作。我们必须在KeyList<T>上实现值相等语义。但是,在这里,我们提出了一个重要的技巧:在将每个项目添加到列表时,我们都会重新计算哈希码,这样我们就不必在以后进行计算。然后,我们将该哈希码用作相等性检查的一部分,以在它们不相等时短路。接下来,让我们看一下该KeyList<T>类的代码。

编码此混乱

sealed class KeyList<T> : IReadOnlyList<T>, IEquatable<KeyList<T>>
{int _hashCode;List<T> _items;public KeyList(){_items = new List<T>();_hashCode = 0;}public KeyList(int capacity){_items = new List<T>(capacity);_hashCode = 0;}public T this[int index] => _items[index];public int Count => _items.Count;public IEnumerator<T> GetEnumerator(){return _items.GetEnumerator();}IEnumerator IEnumerable.GetEnumerator(){return GetEnumerator();}public void Add(T item){_items.Add(item);if (null != item)_hashCode ^= item.GetHashCode();}public override int GetHashCode(){return _hashCode;}public bool Equals(KeyList<T> rhs){if (ReferenceEquals(this, rhs))return true;if (ReferenceEquals(rhs, null))return false;if (rhs._hashCode != _hashCode)return false;var ic = _items.Count;if (ic != rhs._items.Count)return false;for(var i = 0;i<ic;++i)if (!Equals(_items[i], rhs._items[i]))return false;return true;}public override bool Equals(object obj){return Equals(obj as KeyList<T>);}
}

这里的重要方法Add()和主要Equals()方法。您可以在Add()中看到我们正在为添加的每个项目修改_hashCode字段。您可以在Equals()方法中看到,如果哈希代码不相等,我们会在最后进行逐项比较之前短路。这样可以大大提高性能。另一个性能提升在于无需计算任何内容的GetHashCode()方法。由于这些方法通常是由IDictionary<TKey,TValue>实现调用的,因此我们希望它们尽可能快,尤其是GetHashCode()方法,Dictionary<TKey, TValue>经常调用它。

如果在发布模式下运行代码,则将获得准确的性能指标。在Debug中没有那么多,因此如果您在Debug中运行,该程序将向您发出警告。您可以看到我们使用的字典,一个是标准列表,另一个是特殊KeyList<T>实例。与使用List<T>相比,使用KeyList<T>应该可以提高5x+的性能, 而且由于它只实现了列表访问的IReadOnlyList<T>,所以也更安全。现在我们两个最初的问题都解决了。

警告

已经指出,如果T类型是可变的,则这是不正确的。这是真的。然而,Dictionary<TKey, TValue>和HashSet<T>也是如此,所以这里也适用同样的警告-不要更改添加到这些列表的项的值,就像不应该更改添加到Dictionary和HashSet的键值一样。

使用其他容器

如果需要,可以将该技术应用于其他集合。如果需要(相对地)高效的无序比较,可以将其调整为HashSet<T> 。我已经在一些项目中做到了这一点。只需确保在Equals()方法中使用SetEquals()进行逐项比较,因为它更快。

在C#中有效地使用列表作为字典键相关推荐

  1. python字典序列化是什么意思_python序列化中的字符串,列表,字典,类的序列化解释...

    python序列化中的字符串,列表,字典,类的序列化解释 yuaneuro • 2020 年 03 月 07 日 我们知道现在各大语言都有其序列化数据和反序列的方式, 比如php的serialize和 ...

  2. python中元祖 字典 列表的区别_Python中元祖,列表,字典的区别

    原博文 2016-08-16 15:25 − Python中有3种內建的数据结构:列表.元祖和字典: 1.列表 list是处理一组有序项目的数据结构,即你可以在一个列表中存储一个序列的项目. 列表中的 ...

  3. Python列表,元组,字典的区别

    Python列表,元组,字典的区别: 1.元组是不可变的, 而列表.字典是可变的.元组是不可变对象,对象一旦生成,它的值将不能更改:列表是可变对象,对象生成后,可以对其元素进行更改.添加.删除.清空. ...

  4. python如何创建函数对列表里的元素进行分类_zip()函数如何在python中遍历多个列表?...

    在对列表的元素进行找寻时,会频繁的说到遍历的理念.对于复杂的遍历要求,如多个列表中查找就显然不适合用for循环.本篇所要带来的是zip() 函数的方法,能够对多个迭代器进行遍历.下面我们就python ...

  5. python中列表、字典和集合推导式

    1. 列表推导式         ⽤⼀个表达式创建⼀个有规律的列表或控制⼀个有规律列表,又叫列表生成式. 1.1 简单的列表推导式 需求:创建⼀个0-10的列表. 示例代码:[while循环实现] # ...

  6. R语言可视化绘制及PDF使用字体参数列表:查看字体列表、可视化绘制图像中的字体参数列表、字体示例并写入pdf

    R语言可视化绘制及PDF使用字体参数列表:查看字体列表.可视化绘制图像中的字体参数列表.字体示例并写入pdf 目录 R语言可视化绘制及PDF使用字体参数列表:查看字体列表.可视化绘制图像中的字体参数列 ...

  7. pandas使用isin函数和all函数判断dataframe特定数列中是否包含指定列表中的全部内容

    pandas使用isin函数和all函数判断dataframe特定数列中是否包含指定列表中的全部内容(checking if the dataframe column contains all val ...

  8. pandas将dataframe中的内容为列表(list)的数据列裂变、拆分为多个新的数据列实战:拆分为多个新的数据列(并指定新数据列的名称)、数据列中的列表(不等长)拆分为多个新的数据列产生NaN

    pandas将dataframe中的内容为列表(list)的数据列裂变.拆分为多个新的数据列实战:拆分为多个新的数据列(并指定新数据列的名称).数据列中的列表(不等长)拆分为多个新的数据列(产生NaN ...

  9. python for 循环中使用 remove 删除列表中的元素

    python for 循环中使用 remove 删除列表中的元素 错误的代码 # !/usr/bin/python # encoding: utf-8 # -*- coding: utf8 -*- o ...

最新文章

  1. VSCode中.py文件找不到路径的解决办法
  2. 【C 语言】字符串模型 ( strstr-do…while 模型 )
  3. 在js语法中使用jsx标签
  4. 洛谷 P2765 魔术球问题 解题报告
  5. leetcode题库:4.两个排序数组的中位数
  6. list删除重复元素
  7. linux rewind函数,C语言rewind()函数:将文件指针重新指向文件开头
  8. 25个jQuery的编程小抄
  9. 决定系数R2;残差平方和SSE;回归平方和SSR总平方和SST;
  10. 2021 RoboCup机器人中国赛大学组比赛即将开始 智能佳为您提供适合的比赛平台
  11. QSS设置字体透明度
  12. Pyramidal Convolution Rethinking Convolutional Neural Networks for Visual Recognition
  13. 计算机配置高低怎么看,电脑配置高低怎么判断的小技巧分享给大家
  14. linux 压缩减少大小,linux – 为什么gnu parallel chunking会改善gzip的压缩大小?
  15. 设计神经网络的基本原则,神经网络设计与实现
  16. Wi-Fi (-6) 知识点整理
  17. WF(Windows Workflow Foundation)
  18. 十、SpringCloud + Alibaba 全家桶详解(目前公司最新技术)
  19. 拒绝破解 使用中文密码保护RAR文件
  20. 连续周期信号的傅里叶变换(CFT)

热门文章

  1. 数据分析常用的python包_量化投资数据分析之常用的python包(附代码)
  2. ob和mysql兼容吗_OceanBase SQL简介
  3. mysql 主键互换_MySQL-什么方法可以快速的交换两条记录的ID?
  4. UI设计素材 | 备用的404界面,存起来
  5. 流量 起伏大_广西崇左的德天跨国大瀑布,一半是中国一半是越南,天下奇观!...
  6. python默认采用什么字符编码_Python01.2.字符编码-输入输出
  7. C++中内联函数和宏定义的区别
  8. QT实现完整TIM登录框界面样式
  9. Hook Windows API调用 C++
  10. Intel Hyperscan简介