c#集合_键值对Dictionary & SortedList

简介: 在 C# 中,键值对是一种常见的数据结构,可以使用不同的集合类实现。以下是常用的键值对集合类::一种使用哈希表实现的键值对集合。它通过将键哈希为桶号,然后将值存储在桶中进行快速查找。:一种基于数组实现的键值对集合。它会将键值对按照键排序并存储在数组中,以支持快速访问、查找和枚举。:一种使用红黑树实现的键值对集合。它能够按照键的排序进行快速查找,也可以快速地插入和删除键值对,并且该树具备自平衡的特性,使得插入、删除和搜索性能都非常优秀。

前言:

在 C# 中,键值对是一种常见的数据结构,可以使用不同的集合类实现。以下是常用的键值对集合类:

    1. Dictionary<TKey, TValue>:一种使用哈希表实现的键值对集合。它通过将键哈希为桶号,然后将值存储在桶中进行快速查找。
      1. SortedList<TKey, TValue>:一种基于数组实现的键值对集合。它会将键值对按照键排序并存储在数组中,以支持快速访问、查找和枚举。
        1. SortedDictionary<TKey, TValue>:一种使用红黑树实现的键值对集合。它能够按照键的排序进行快速查找,也可以快速地插入和删除键值对,并且该树具备自平衡的特性,使得插入、删除和搜索性能都非常优秀。
          1. ConcurrentDictionary<TKey, TValue>:一种线程安全的键值对集合,它支持在高并发场景下的快速查找、插入和删除,并且保证线程安全。
            1. ImmutableDictionary<TKey, TValue>:一种不可变的键值对集合,它提供了对集合进行修改时创建新集合的效率优化,同时也是线程安全的。
              1. KeyValuePair<TKey, TValue>:一个用于表示键值对的结构体类型。它将一个键和一个值组合在一起,并提供了多种方法来处理这个组合。

              在工作中比较经常用到Dictionary 和SortedList。

              SortedList

              var list = new SortedList<string, int>();
                      //使用[]方式添加键值对时若该键不参加就添加,存在就修改对应的值
                      list["芋泥波波"] = 16;
                      list["黄桃果霸"] = 16;
                      //使用Add添加键值对时,键不能重复,否则报错
                      list.Add("杨枝甘露", 19);
                      list.Add("满杯烧仙草", 21);

              image.gif

              遍历获取键值,元素被检索为keyvaluePair对象

              foreach (var VARIABLE in list)
                      {
                          Console.WriteLine(VARIABLE.Key + "  价格:" + VARIABLE.Value);
                      }
              //输出:
                  黄桃果霸  价格:16
                  满杯烧仙草  价格:21
                  杨枝甘露  价格:19
                  芋泥波波  价格:16

              image.gif

              获取值

              //通过键获取值
                      Console.WriteLine("杨枝甘露的价格:" + list["杨枝甘露"]);
                      //通过索引获取值
                      Console.WriteLine("芋泥波波的价格:" + list.Values[0]);
              //输出:
                  杨枝甘露的价格:19
                  芋泥波波的价格:16
                      //获取集合中的Values列表(若要获取所有的键则Values改成Keys)
                      IList<int> values = list.Values;
                      foreach (var value in values)
                      {
                          Console.WriteLine(value);
                      }
              输出:
                      16
                      21
                      19
                      16

              image.gif

              如果在通过键获取值时,不确定该键值对是否存在,那么就使用TryGetValue,这样能避免键不存在时报异常

              list.TryGetValue("黑糖珍珠", out int result);
                      Console.WriteLine(result);
              输出:
                      0

              image.gif

              判断

              //判断是否包含某个键
                      if (list.ContainsKey("芋泥波波"))
                      {
                          Console.WriteLine("包含");
                      }
                      else
                      {
                          Console.WriteLine("不包含");
                      }
              输出:
                     包含

              image.gif

              移除

              //通过键移除元素
                      list.Remove("芋泥波波");

              image.gif

              HashTable

              var hashtable = new Hashtable();
                      hashtable.Add("杨枝甘露", 19);
                      hashtable.Add(21,"满杯烧仙草");

              image.gif

              可以看出,使用HashTable时不需要设置类型,因此它是非泛型的

                1. 缺点:非泛型,需要装箱拆箱,效率较低,类型非安全,
                  1. 好处:线程安全,当线程并发时,只有一个线程会执行这段代码

                  字典Dictionary

                  Dictionary与hashtable相反

                    1. 缺点:线程非安全
                      1. 优点:类型安全,效率高
                      IDictionary<string, int> dictionary = new Dictionary<string, int>();
                              dictionary["芋泥波波"] = 16;
                              dictionary["黄桃果霸"] = 16;
                              dictionary.Add("杨枝甘露", 19);
                              dictionary.Add("满杯烧仙草", 21);
                              foreach (var VARIABLE in dictionary)
                              {
                                  Console.WriteLine(VARIABLE.Key + "  价格:" + VARIABLE.Value);
                              }
                      输出:
                          芋泥波波  价格:16
                          黄桃果霸  价格:16
                          杨枝甘露  价格:19
                          满杯烧仙草  价格:21

                      image.gif

                      可以看出IDictionary和SortedList的方法用法都大致相同,虽然它们提供的方法有些相似,但它们在实现上有所不同,比如 IDictionary 使用哈希表进行实现,而 SortedList 则使用了基于数组排序的实现。因此在某些场景下,它们的运行效率和行为可能有所不同。

                      这里列举一下IDictionary和SortedList方法上的差异

                        1. 添加方法

                        IDictionary 提供了 Add(TKey, TValue) 方法来添加一个键值对。如果已经存在一个具有相同键的元素,则会抛出异常。

                        SortedList 提供了 Add(TKey, TValue) 方法来添加一个键值对。如果键已经存在,则会用新值替换旧值。

                          1. 删除方法

                          IDictionary 提供了 Remove(TKey) 方法来移除一个键值对。如果不存在具有指定键的元素,则不会进行任何操作。

                          SortedList 提供了 Remove(TKey) 方法来移除具有指定键的键值对。

                            1. 更新方法

                            IDictionary 提供了一种索引器,允许使用键指定元素来更新值。如果指定键不存在,则将添加新元素。

                            SortedList 提供了同样的索引器,以便根据键更新值。

                              1. 访问方法

                              IDictionary 提供了 Keys 和 Values 属性,分别返回一个包含所有键和值的集合。

                              SortedList 提供了 Keys 和 Values 属性,分别返回一个按键或值进行排序的数组。

                              IDictionary和SortedList的区别

                                1. 数据结构

                                IDictionary 是一种键值对集合,其中每个元素都是由一个键和一个值组成的。可以使用键来访问集合中的值。键在字典中需要是唯一的。

                                SortedList 基于一个排序列表实现,其中每个元素都是一个键值对。所有元素按照键进行排序,使得可以根据键进行快速的查找和访问。如同一个数组,键必须是唯一的。

                                  1. 访问

                                  通过键访问 IDictionary 中的元素比通过索引访问 SortedList 中的元素要慢一些。因为需要使用哈希表来查找键值对。

                                  SortedList 中的元素可以通过索引(其键在列表中的顺序)进行访问。因为元素已经按键排序,访问仅仅需要进行集合中的索引操作。

                                    1. 效率

                                    IDictionary 的查找和修改操作通常比 SortedList 更快,因为 IDictionary 使用哈希表来快速查找(使用 Dictionary 类时)和修改。

                                    SortedList 中的所有元素都必须按键进行排序,因此插入和删除操作通常比哈希表慢,但与数组相比较快。

                                      1. 内存使用

                                      IDictionary 存储的数据结构通常使用哈希表,哈希表需要更多的内存用于存储哈希值和链表的指针。因此,IDictionary 需要比 SortedList 更多的内存。

                                      SortedList 使用一个数组来存储元素,所以内存使用相对较少。

                                        1. 用途

                                        IDictionary 适用于在集合中查找特定的值。每个键都映射到一个值,可以通过键来快速查找并获取值。它也可以被用来存储和管理任何需要一个唯一标识的对象集合。

                                        SortedList 适用于需要按照键的顺序进行访问的集合。所有元素都按照键排序,允许按键顺序进行快速的访问。它也可用于需要实现一个简单的排序算法来排序对象集合的情况,比如一个按名称排序的集合。

                                        ConcurrentDictionary

                                        针对于Dictionary的线程非安全,官方提供了ConcurrentDictionary,ConcurrentDictionary是线程安全的,但如果只是进行读取操作推荐使用Dictionary,涉及到读写才使用ConcurrentDictionary ,因为ConcurrentDictionary的效率会更慢

                                        ConcurrentDictionary<string, int> dict = new();
                                              dict["杨枝甘露"] = 16;
                                              //尝试添加,字段如果存在就不做处理,也不会抛异常
                                              dict.TryAdd("杨枝甘露", 19);
                                              Console.WriteLine(dict["杨枝甘露"]);     //16

                                        image.gif

                                        方法文档:

                                        向字典添加新键(如果字典中尚不存在)

                                        TryAdd

                                        如果字典中当前不存在键,此方法将添加指定的键/值对。 方法返回 true 或 false ,具体取决于是否添加了新对。

                                        更新字典中现有键的值(如果该键具有特定值)

                                        TryUpdate

                                        此方法检查键是否具有指定的值,如果具有指定值,则使用新值更新密钥。 它类似于 CompareExchange 方法,只不过它用于字典元素。

                                        无条件地将键/值对存储在字典中,并覆盖已存在的键的值

                                        索引器资源库: dictionary[key] = newValue

                                        将键/值对添加到字典,或者如果键已存在,请根据键的现有值更新键的值

                                        AddOrUpdate(TKey, Func<TKey,TValue>, Func<TKey,TValue,TValue>)

                                        - 或 -

                                        AddOrUpdate(TKey, TValue, Func<TKey,TValue,TValue>)

                                        AddOrUpdate(TKey, Func<TKey,TValue>, Func<TKey,TValue,TValue>) 接受密钥和两个委托。 如果字典中不存在键,则使用第一个委托;它接受密钥并返回应为键添加的值。 如果键确实存在,则使用第二个委托;它接受键及其当前值,并返回应为键设置的新值。

                                        AddOrUpdate(TKey, TValue, Func<TKey,TValue,TValue>) 接受键、要添加的值和更新委托。 这与上一个重载相同,只不过它不使用委托来添加键。

                                        获取字典中某个键的值,将该值添加到字典中,如果该键不存在,则返回该值

                                        GetOrAdd(TKey, TValue)

                                        - 或 -

                                        GetOrAdd(TKey, Func<TKey,TValue>)

                                        这些重载为字典中的键/值对提供延迟初始化,仅当值不存在时才添加该值。

                                        GetOrAdd(TKey, TValue) 如果键不存在,则采用要添加的值。

                                        GetOrAdd(TKey, Func<TKey,TValue>) 采用一个委托,该委托将在键不存在时生成值。

                                        所有这些操作都是原子操作,对于 类上的 ConcurrentDictionary<TKey,TValue> 所有其他操作都是线程安全的唯一的例外是接受委托的方法,即 AddOrUpdateGetOrAdd。 对于对字典的修改和写入操作, ConcurrentDictionary<TKey,TValue> 请使用精细锁定来确保线程安全。 (以无锁方式对字典执行读取操作。) 但是,这些方法的委托在锁外部调用,以避免在锁下执行未知代码时可能出现的问题。 因此,这些委托执行的代码不受操作原子性的约束。

                                        如下是接送委托的线程非安全方法👇

                                        //增加或修改,如果不存在,增加键值对的值为10,存在则修改为99
                                              dict.AddOrUpdate("满杯烧仙草", 10,(k,v)=>{return 99;});
                                              Console.WriteLine(dict["满杯烧仙草"]);    //10
                                              dict.AddOrUpdate("满杯烧仙草", 10,(k,v)=>{return 99;});
                                              Console.WriteLine(dict["满杯烧仙草"]);    //99
                                              //如果存在则获取值,不存在就增加
                                              dict.GetOrAdd("黄桃果霸", 17);
                                              Console.WriteLine(dict["黄桃果霸"]);    //17
                                              dict.GetOrAdd("黄桃果霸", 99);
                                              Console.WriteLine(dict["黄桃果霸"]);    //17

                                        image.gif

                                        目录
                                        相关文章
                                        |
                                        7月前
                                        |
                                        SQL 开发框架 .NET
                                        C#进阶-LINQ实现对集合的增删改查
                                        本篇演示了LINQ在日常开发中的常用操作,实现结果集的增删改查。目前LINQ支持两种语法,我会在每个案例前先用大家熟知的SQL语句表达,再在后面用C#的两种LINQ语法分别实现。LINQ语法第一次接触难免感到陌生,最好的学习方式就是在项目中多去使用,相信会有很多感悟。
                                        96 0
                                        |
                                        7月前
                                        |
                                        存储 人工智能 C#
                                        【Unity 3D】C#中数组、集合、栈、队列、哈希表、字典的讲解(附测试代码)
                                        【Unity 3D】C#中数组、集合、栈、队列、哈希表、字典的讲解(附测试代码)
                                        95 0
                                        |
                                        1月前
                                        |
                                        Java 物联网 C#
                                        C#/.NET/.NET Core学习路线集合,学习不迷路!
                                        C#/.NET/.NET Core学习路线集合,学习不迷路!
                                        |
                                        3月前
                                        |
                                        存储 C# 索引
                                        C# 一分钟浅谈:数组与集合类的基本操作
                                        【9月更文挑战第1天】本文详细介绍了C#中数组和集合类的基本操作,包括创建、访问、遍历及常见问题的解决方法。数组适用于固定长度的数据存储,而集合类如`List<T>`则提供了动态扩展的能力。文章通过示例代码展示了如何处理索引越界、数组长度不可变及集合容量不足等问题,并提供了解决方案。掌握这些基础知识可使程序更加高效和清晰。
                                        86 2
                                        |
                                        2月前
                                        |
                                        开发框架 NoSQL MongoDB
                                        C#/.NET/.NET Core开发实战教程集合
                                        C#/.NET/.NET Core开发实战教程集合
                                        |
                                        3月前
                                        |
                                        SQL 开发框架 安全
                                        并发集合与任务并行库:C#中的高效编程实践
                                        在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue&lt;T&gt;`和`ConcurrentDictionary&lt;TKey, TValue&gt;`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
                                        56 1
                                        |
                                        4月前
                                        |
                                        存储 C# 索引
                                        C# 集合语法全解
                                        C# 集合语法全解
                                        35 0
                                        |
                                        7月前
                                        |
                                        存储 安全 Java
                                        34.C#:listT泛型集合
                                        34.C#:listT泛型集合
                                        63 1
                                        |
                                        7月前
                                        |
                                        存储 C#
                                        33.c#:hashtable集合
                                        33.c#:hashtable集合
                                        46 1
                                        |
                                        7月前
                                        |
                                        存储 安全 Java
                                        32.C#:ArrayList集合
                                        32.C#:ArrayList集合
                                        43 1