美文网首页
C# 常用数据结构

C# 常用数据结构

作者: 合肥黑 | 来源:发表于2022-01-27 20:08 被阅读0次
    一、c#中的数组、ArrayList、List区别

    在C#中数组,ArrayList,List都能够存储一组对象,那么这三者到底有什么样的区别呢。

    • (1)数组引入的命名空间:using System;
    • (2)Array:用法基本与数组同,引入命名空间:using System;
    • (3)ArrayList:引入命名空间: using System.Collections
    • (4)List:引入命名空间:using System.Collections.Generic;
    1.数组

    数组在C#中最早出现的。在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单。

    //string[] s= {"a","b"};
    string[] s=new string[2];  
      
    //赋值  
    s[0]="a";  
    s[1]="b";  
    //修改  
    s[1]="a1";  
    

    但是数组存在一些不足的地方。在数组的两个数据间插入数据是很麻烦的,而且在声明数组的时候必须指定数组的长度,数组的长度过长,会造成内存浪费,过段会造成数据溢出的错误。如果在声明数组时我们不清楚数组的长度,就会变得很麻烦。针对数组的这些缺点,C#中最先提供了ArrayList对象来克服这些缺点。

    2.ArrayList

    ArrayList是命名空间System.Collections下的一部分,在使用该类时必须进行引用,同时继承了IList接口,提供了数据存储和检索。ArrayList对象的大小是按照其中存储的数据来动态扩充与收缩的。所以,在声明ArrayList对象时并不需要指定它的长度。

    ArrayList list1 = new ArrayList();  
    //新增数据  
    list1.Add("cde");  
    list1.Add(5678);  
    //修改数据  
    list[2] = 34;  
    //移除数据  
    list.RemoveAt(0);  
    //插入数据  
    list.Insert(0, "qwe");  
    

    从上面例子看,ArrayList好像是解决了数组中所有的缺点,为什么又会有List?

    我们从上面的例子看,在List中,我们不仅插入了字符串cde,而且插入了数字5678。这样在ArrayList中插入不同类型的数据是允许的。因为ArrayList会把所有插入其中的数据当作为object类型来处理,在我们使用ArrayList处理数据时,很可能会报类型不匹配的错误,也就是ArrayList不是类型安全的。在存储或检索值类型时通常发生装箱和取消装箱操作,带来很大的性能耗损。

    装箱与拆箱的概念:

    装箱:就是将值类型的数据打包到引用类型的实例中 比如将string类型的值abc赋给object对象obj

    String  i=”abc”;
    object obj=(object)i;
    

    拆箱:就是从引用数据中提取值类型:比如将object对象obj的值赋给int类型的变量j

    object obj=”abc”;
    int j = (int)obj;//拆箱:将引用类型的obj转化为值类型j;
    

    补充:
    Array的用法与数组几乎一样,可以看做是数组。在定义的时候需要指定长度;ArrayList的用法与普通集合一样,定义的时候不需要指定长度;如:

    Array[] animalArray = new Array[2];
    ArrayList animalArrayList = new ArrayList();
    
    3.泛型List

    因为ArrayList存在不安全类型与装箱拆箱的缺点,所以出现了泛型的概念。List类是ArrayList类的泛型等效类,它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。

    list = new List<string>();  
    //新增数据  
    list.Add(“abc”);  
    //修改数据  
    list[0] = “def”;  
    //移除数据  
    list.RemoveAt(0);  
    

    上例中,如果我们往List集合中插入int数组123,IDE就会报错,且不能通过编译。这样就避免了前面讲的类型安全问题与装箱拆箱的性能问题了。

    二、C# ArrayList、HashSet、HashTable、List、Dictionary的区别详解

    HashTable是一种根据key查找非常快的键值数据结构,不能有重复key,而且由于其特点,其长度总是一个素数,所以扩容后容量会比2倍大一点点,加载因子为0.72f。

    当要大量使用key来查找value的时候,HashTable无疑是最有选择,HashTable与ArrayList一样,是非泛型的,value存进去是object,存取会发生装箱、拆箱,所以出现了Dictionary。

    Dictionary是HashTable的泛型版本,存取同样快,但是不需要装箱和拆箱了。而且,其优化了算法,Hashtable是0.72,它的浪费容量少了很多。

    1.HashTable
    using System;
    using System.Collections;
    
    namespace CollectionsApplication
    {
       class Program
       {
          static void Main(string[] args)
          {
             Hashtable ht = new Hashtable();
    
    
             ht.Add("001", "Zara Ali");
             ht.Add("002", "Abida Rehman");
             ht.Add("003", "Joe Holzner");
             ht.Add("004", "Mausam Benazir Nur");
             ht.Add("005", "M. Amlan");
             ht.Add("006", "M. Arif");
             ht.Add("007", "Ritesh Saikia");
    
             if (ht.ContainsValue("Nuha Ali"))
             {
                Console.WriteLine("This student name is already in the list");
             }
             else
             {
                ht.Add("008", "Nuha Ali");
             }
             // 获取键的集合 
             ICollection key = ht.Keys;
    
             foreach (string k in key)
             {
                Console.WriteLine(k + ": " + ht[k]);
             }
             Console.ReadKey();
          }
       }
    }
    
    001: Zara Ali
    002: Abida Rehman
    003: Joe Holzner
    004: Mausam Benazir Nur
    005: M. Amlan
    006: M. Arif
    007: Ritesh Saikia
    008: Nuha Ali 
    
    2.Dictionary
    //Persion.cs
    using System;
    
    namespace SampleList
    {
        class Person
        {
            public string name;
            public int age;
    
            //构造函数
            public Person(string name, int age)
            {
                this.name = name;
                this.age = age;
            }
        }
    }
    
    //CustomDictionary.cs
    using System;
    using System.Collections.Generic;
    
    namespace SampleList
    {
        class CustomDictionary
        {
    
            //定义一个字典变量
            static Dictionary<int, Person> dicPerson = new Dictionary<int, Person>();
    
            public static void LearnDictionaryInfo()
            {
                //添加键值 
                Person p1 = new Person("hjc", 22);
                Person p2 = new Person("tf", 21);
                dicPerson.Add(0, p1); //方式1
                dicPerson[1] = p2;    //方式2
    
                //取值
                Console.WriteLine("\n");
                Console.WriteLine("取值  name:" + dicPerson[0].name + "—" + "age:" + dicPerson[0].age);
    
                //改值
                Console.WriteLine("\n");
                dicPerson[1].age = 20;
                Console.WriteLine("改值  name:" + dicPerson[1].name + "—" + "age:" + dicPerson[1].age);
    
                //遍历key
                Console.WriteLine("\n");
                Console.WriteLine("遍历 key");
                foreach (int key in dicPerson.Keys)
                {
                    string id = "用户ID:" + key;
                    string str = string.Format("name:{0} age:{1}", dicPerson[key].name, dicPerson[key].age);
                    Console.WriteLine(id + "\t" + str);
                }
    
                //遍历value
                Console.WriteLine("\n");
                Console.WriteLine("遍历 value");
                foreach (Person value in dicPerson.Values)
                {
                    string str = string.Format("name:{0} age:{1}", value.name, value.age);
                    Console.WriteLine(str);
                }
    
                //遍历字典
                Console.WriteLine("\n");
                Console.WriteLine("遍历字典");
                foreach (KeyValuePair<int, Person> kvp in dicPerson)
                {
                    string str = string.Format("key:{0}/name:{1}/age:{2}", kvp.Key, kvp.Value.name, kvp.Value.age);
                    Console.WriteLine(str);
                }
    
                //  删除元素
                Console.WriteLine("\n");
                Console.WriteLine("删除元素");
                if (dicPerson.ContainsKey(1))    //如果存在
                    dicPerson.Remove(1);
                foreach (Person value in dicPerson.Values)
                {
                    string str = string.Format("name:{0} age:{1}", value.name, value.age);
                    Console.WriteLine(str);
                }
                //清除所有的元素
                dicPerson.Clear();
    
                Console.Read();
            }
    
        }
    }
    
    3.HashSet

    HashSet类,算法,存储结构都与哈希表相同,主要是设计用来做高性能集运算的,例如对两个集合求交集、并集、差集等。集合中包含一组不重复出现且无特定顺序的元素。

    参考
    如何使用 C# 中的 HashSet

    所谓的HashSet,指的就是 System.Collections.Generic 命名空间下的 HashSet<T> 类,它是一个高性能,无序的集合,因此HashSet它并不能做排序操作,也不能包含任何重复的元素,Hashset 也不能像数组那样使用索引,所以在 HashSet 上你无法使用 for 循环,只能使用 foreach 进行迭代,HashSet 通常用在处理元素的唯一性上有着超高的性能。

    HashSet<T> 实现了如下几个接口:

    
    public class HashSet<T> : System.Collections.Generic.ICollection<T>,
    System.Collections.Generic.IEnumerable<T>, 
    System.Collections.Generic.IReadOnlyCollection<T>,
    System.Collections.Generic.ISet<T>,
    System.Runtime.Serialization.IDeserializationCallback,
    System.Runtime.Serialization.ISerializable
    {
    }
    
    

    HashSet 只能包含唯一的元素,它的内部结构也为此做了专门的优化,值得注意的是,HashSet 也可以存放单个的 null 值,可以得出这么一个结论:如何你想拥有一个具有唯一值的集合,那么 HashSet 就是你最好的选择,何况它还具有超高的检索性能。

    
            static void Main(string[] args)
            {
                HashSet<string> hashSet = new HashSet<string>();
                hashSet.Add("A");
                hashSet.Add("B");
                hashSet.Add("C");
                hashSet.Add("D");
                if (hashSet.Contains("D"))
                    Console.WriteLine("The required element is available.");
                else
                    Console.WriteLine("The required element isn’t available.");
                Console.ReadKey();
            }
    

    如果你向 HashSet 中插入重复的元素,它的内部会忽视这次操作而不像别的集合一样抛出异常,接下来展示一下代码:

    
            static void Main(string[] args)
            {
                HashSet<string> hashSet = new HashSet<string>();
                hashSet.Add("A");
                hashSet.Add("B");
                hashSet.Add("C");
                hashSet.Add("D");
                hashSet.Add("D");
                Console.WriteLine("The number of elements is: {0}", hashSet.Count);//4
                Console.ReadKey();
            }
    
    4.Queue、Queue

    Queue队列,Queue泛型队列,大学都学过,队列,先进先出,很有用。

    5.Stack、Stack

    Stack堆栈,先进后出。

    6.SortedList、SortedList

    SortedList集合中的数据是有序的。可以通过key来匹配数据,也可以通过int下标来获取数据。

    添加操作比ArrayList,Hashtable略慢;查找、删除操作比ArrayList快,比Hashtable慢。

    7.SortedDictionary

    SortedDictionary相比于SortedList其性能优化了,SortedList其内部维护的是数组而SortedDictionary内部维护的是红黑树(平衡二叉树)的一种,因此其占用的内存,性能都好于SortedDictionary。唯一差在不能用下标取值。

    8.ListDictionary(单向链表),LinkedList(双向链表)

    ,ArrayList,Hashtable等容器类,其内部维护的是数组Array来,ListDictionary和LinkedList不用Array,而是用链表的形式来保存。链表最大的好处就是节约内存空间。

    ListDictionary是单向链表。

    LinkedList双向链表。双向链表的优势,可以插入到任意位置。

    参考C#LinkedList<T>链表

    static void Main(string[] args)
    {
        LinkedList<int> a = new LinkedList<int>();   //创建一个链表
        a.AddFirst(3);                         //在一开始添加一个节点
        a.AddLast(1);                          //在最后添加一个节点
        a.AddLast(4);                          //在最后添加一个节点
    
        foreach (int i in a)
            Console.Write(i + " ");            //输出3 1 4
        Console.WriteLine();
    
        LinkedListNode<int> cur = a.Find(3);   //cur对应3所在的第一个位置
        if (cur != null)
        {
            a.AddAfter(cur, 2);                //在3后面添加2
            a.AddBefore(cur,5);                //在3前面添加5
        }
    
        foreach (int i in a)
            Console.Write(i + " ");            //输出5 3 2 1 4
        Console.WriteLine();
    
        Console.WriteLine(cur.Next.Value);     //cur当前为3的位置,所以下一个为2
        Console.WriteLine(cur.Next.Previous.Value);  //3的下一个为2,2的上一个仍然为3
        Console.WriteLine(cur.Previous);       //Previous为上一个节点
    
        a.RemoveFirst();
        foreach (int i in a)
            Console.Write(i + " ");            //返回3 2 1 4
        Console.WriteLine();
    
        a.RemoveLast();
        foreach (int i in a)
            Console.Write(i + " ");            //返回3 2 1 
        Console.WriteLine();
    
        a.Remove(3);                           //删除值为3的节点,成功返回true,否则false
        a.Clear();                             //清空所有的节点
    
        Console.Read();
    }
    
    9.HybridDictionary

    HybridDictionary的类,充分利用了Hashtable查询效率高和ListDictionary占用内存空间少的优点,内置了Hashtable和ListDictionary两个容器,添加数据时内部逻辑如下:

    当数据量小于8时,Hashtable为null,用ListDictionary保存数据。

    当数据量大于8时,实例化Hashtable,数据转移到Hashtable中,然后将ListDictionary置为null。

    10.BitArray

    BitArray这个东东是用于二进制运算,"或"、"非"、"与"、"异或非"等这种操作,只能存true或false;

    相关文章

      网友评论

          本文标题:C# 常用数据结构

          本文链接:https://www.haomeiwen.com/subject/kuxtzltx.html