常见数据结构补充

本文详细介绍了C#中的数据结构,如数组、ArrayList、LinkedList、HashSet、SortedSet、Hashtable、Dictionary等,以及它们的特点、操作性能和适用场景。还探讨了迭代器模式和线程安全版本的集合。
摘要由CSDN通过智能技术生成


数据结构

Set集合:纯粹的容器,无序存储
线性结构:在存储的时候,一对一存储。例如:Array/ArrayList/List/LinkedList/Queue/Stack/HastSet/SortedSet/Hashtable/SortedList/Dictionary/SortedDictionary
/// IEnumerable、ICollection、IList、IQueryable
树形结构:类似表达式目录树(二叉树),菜单结构,一对多
图状结构:拓扑图、网状结构(地图开发,用的上)


提示:以下是本篇文章正文内容,下面案例可供参考

一、数组

1.Array

数组特点:内存连续存储,节约空间,可以索引访问,读取快,删慢

int[] intArray = new int[3];
intArray[0] = 123;
string[] stringArray = new string[] { "111", "222" };

2.ArrayList

ArrayList:以前的开发中使用的比较多 不定长的,连续分配的.元素没有类型限制,任何元素都是当成object处理,如果是值类型,会有装箱操作。读取快,增删慢。

ArrayList arrayList = new ArrayList();
arrayList.Add("test");
arrayList.Add("aaaa");
arrayList.Add(32);//add增加长度
var value = arrayList[2];//可以通过索引访问
arrayList.RemoveAt(0); //可以通过索引删除
arrayList.Remove("test"); //可以通过数据删除

3.List

List:也是Array,内存上都是连续摆放;不定长;泛型,保证类型安全,避免装箱拆箱; 性能也是比Arraylist要高。读取快,增删慢。

List<int> IntList = new List<int>() { 1, 2, 3, 4 };
IntList.Add(123);
IntList.Add(123);
List<string> StringList = new List<string>();
StringList .Add("123");

二、链表

链表是非连续摆放的,存储数据+地址,找数据的话就只能顺序查找,读取慢;增删快,

1.LinkedList

LinkedList:泛型的特点;链表,元素不连续分配,每个元素都有记录前后节点,节点值可以重复,不能通过索引访问,查询元素就只能遍历,所以查询性能较慢。

LinkedList<int> linkedList = new LinkedList<int>();
//linkedList[3] //不能索引访问--不是数组
linkedList.AddFirst(111);//在最前面添加
linkedList.AddLast(222);  //在最后添加

bool isContain = linkedList.Contains(111);
LinkedListNode<int> node111 = linkedList.Find(111);  //元素123的位置  从头查找

linkedList.AddBefore(node111, 123);
linkedList.AddBefore(node111, 123);
linkedList.AddAfter(node111, 9);

linkedList.Remove(456);
linkedList.Remove(node111);
linkedList.RemoveFirst();
linkedList.RemoveLast();
linkedList.Clear();

2.Queue

Queue:队列,就跟一个没有瓶底的瓶子一样; 就是链表 先进先出 放任务延迟执行,A不断写入日志任务 B不断获取任务去执行。
ConcurrentQueue 线程安全版本的Queue

Queue<string> numbers = new Queue<string>();
numbers.Enqueue("1");
numbers.Enqueue("2");
numbers.Enqueue("3");
numbers.Enqueue("4");
numbers.Enqueue("5");
numbers.Enqueue("6");

numbers.Dequeue();//移除第一条并返回它的值
numbers.Peek();//返回第一条队列的值
queueCopy.Contains("1");是否包含某个元素,返回True/False
queueCopy.Clear();清空
queueCopy.Count;返回队列条数

在这里插入图片描述

3.Stack

Stack:就是链表 先进后出 解析表达式目录树的时候,先产生的数据后使用。
ConcurrentStack线程安全版本的Stack

Stack<string> numbers = new Stack<string>();
numbers.Push("1");
numbers.Push("2");
numbers.Push("3");
numbers.Push("4");
numbers.Push("5");//放进去

foreach (string number in numbers)
{
    Console.WriteLine(number);
}

Console.WriteLine($"Pop '{numbers.Pop()}'");//获取并移除
Console.WriteLine($"Peek at next item to dequeue: {numbers.Peek()}");//获取不移除
Console.WriteLine($"Pop '{numbers.Pop()}'");

Stack<string> stackCopy = new Stack<string>(numbers.ToArray());
foreach (string number in stackCopy)
{
    Console.WriteLine(number);
}
Console.WriteLine($"stackCopy.Contains(\"2\") = {stackCopy.Contains("2")}");
stackCopy.Clear();
Console.WriteLine($"stackCopy.Count = {stackCopy.Count}");
Console.Read();

在这里插入图片描述

三、Hash

1.HashSet

HashSet集合:hash分布,元素间没关系,动态增加容量,特点是能够去重,对于对象去重只能在同一个引用下才能去重。

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("123");
hashSet.Add("689");
hashSet.Add("456");
string s1 = "12345";
hashSet.Add(s1);
string s2 = "12345";
hashSet.Add(s2);
string s3 = "12345";
hashSet.Add(s3);
//hashSet[0];
foreach (var item in hashSet)
{
    Console.WriteLine(item);
}
Console.WriteLine(hashSet.Count);
Console.WriteLine(hashSet.Contains("12345"));

HashSet<string> hashSet1 = new HashSet<string>();
hashSet1.Add("123");
hashSet1.Add("689");
hashSet1.Add("789");
hashSet1.Add("12435");
hashSet1.Add("12435");
hashSet1.Add("12435");
hashSet1.SymmetricExceptWith(hashSet);//取补集,(两个集合除开交集部分的数据)
hashSet1.UnionWith(hashSet);//取并集
hashSet1.ExceptWith(hashSet);//取差集
hashSet1.IntersectWith(hashSet);//取交集

同引用去重演示:
在这里插入图片描述

2.排序的集合SortedSet

排序且去重,与HashSet差不多,就是多了排序功能

SortedSet<string> sortedSet = new SortedSet<string>();
sortedSet.Add("123");
sortedSet.Add("689");
sortedSet.Add("456");
sortedSet.Add("12435");
sortedSet.Add("12435");
sortedSet.Add("12435");

四、字典

key-value,一段连续有限空间放value(开辟的空间比用到的多,hash是用空间换性能),基于key散列计算得到地址索引,这样读取快,增删也快,删除时也会计算位置,都不会影响其他元素,因为key 是最终生成了索引的。
缺点:如果数量过多,散列计算后,肯定会出现不同的key计算出的索引只是同一个,散列结果一致18,可以让第二次的+1,可能会造成效率的降低,尤其是数据量大的情况下,dictionary在3w条左右性能就开始下降的厉害。

扩展:
hash散列:散列(Hashing)是一种将任意大小的数据映射为固定大小的值的过程。散列函数(Hash Function)负责执行这个映射。Hash散列是指使用散列函数将数据映射为散列值的过程。
散列函数接受输入(通常是任意长度的数据),并输出固定长度的散列值。这个过程的目标是使得不同的输入产生不同的散列值,并且散列值的分布尽可能均匀,以防止冲突(多个不同的输入映射到相同的散列值)。Hash散列在计算机科学中有许多应用,其中一项重要的应用是在哈希表(Hash Table)中。哈希表是一种数据结构,它使用散列函数将键映射到特定的索引位置,从而加速数据的查找和检索过程。通过使用散列函数,可以快速定位存储或检索数据的位置,而不必遍历整个数据集。

良好的散列函数应该具备:
一致性: 对于相同的输入,散列函数应该始终产生相同的输出。
高效性: 计算散列值的过程应该是高效的,即使对于大型数据集也是如此。
均匀性: 输入空间中的不同值应该被映射到输出空间中的不同值,而且最好是均匀分布。
抗碰撞性: 不同的输入应该映射到不同的散列值,以最小化冲突的发生。

1.Hashtable

Hashtable体积可以动态增加 拿着key计算一个地址,然后放入key - value,同时浪费了空间,Hashtable是基于数组实现,查找和增删都是一次定位,增删查改 都很快。如果数据太多,重复定位定位,效率就下去了

Hashtable table = new Hashtable();
table.Add("123", "456");
//table.Add("123", "456");//key相同  会报错
table[234] = 456;
table[234] = 567;
table[32] = 4562;
table[1] = 456;
table["test"] = 456;
foreach (DictionaryEntry objDE in table)
{
    Console.WriteLine(objDE.Key.ToString());
    Console.WriteLine(objDE.Value.ToString());
}
//线程安全
Hashtable.Synchronized(table);//只有一个线程写  多个线程读

2.Dictionary

ConcurrentDictionary线程安全的Dictionary
ConcurrentBag线程安全的对象集合

Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "1");
dic.Add(5, "2");
dic.Add(3, "3");

ConcurrentDictionary<int, string> dic = new ConcurrentDictionary<int, string>();
dic.TryAdd(1, "1");
dic.TryAdd(5, "2");
dic.TryAdd(3, "3");

//可以帮你进行排序的
SortedDictionary<int, string> dic = new SortedDictionary<int, string>();
dic.Add(1, "asd");
dic.Add(5, "ddd");
dic.Add(3, "fff");
dic.Add(2, "vvv");

SortedList sortedList = new SortedList();
sortedList.Add("1", "Hello");
sortedList.Add("2", "World");
sortedList.Add("3", "!");
sortedList["Fourth"] = "!!!";

var keyList = sortedList.GetKeyList();
var valueList = sortedList.GetValueList();
sortedList.TrimToSize();//用于最小化集合的内存开销
sortedList.Remove("2");
sortedList.RemoveAt(0);
sortedList.Clear();

sortedList.Add("3", "!");//重复的Key Add会错

五、迭代器模式

数据集合之所以能够支持Foreach循环,其实都是通过实现IEnumerable接口来完成,就需要实现一个GetIEnumerator方法;这个方法返回的是一个Enumerator(迭代器);迭代器就是提供了统一对线型结构数据的一种访问方式。

1.Iterator

下面使用举例说明:

//声明一个迭代器结构
    public interface IIterator<T>
    {
        /// <summary>
        /// 当前的对象
        /// </summary>
        T Current { get; }
        /// <summary>
        /// 移动到下一个对象,是否存在
        /// </summary>
        /// <returns></returns>
        bool MoveNext();
        /// <summary>
        /// 重置
        /// </summary>
        void Reset();
    }
    public class Food
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Price { get; set; }
    }
    public class AIterator : IIterator<Food>
    {
        private Food[] _FoodList = null;

        public AIterator(AMenu kfcMenu)
        {
            this._FoodList = kfcMenu.GetFoods();
        }

        private int _CurrentIndex = -1;
        public Food Current
        {
            get
            {
                return this._FoodList[_CurrentIndex];
            }
        }
        public bool MoveNext()
        {
            return this._FoodList.Length > ++this._CurrentIndex;
        }
        public void Reset()
        {
            this._CurrentIndex = -1;
        }
    }

    public class BMenuIterator : IIterator<Food>
    {
        private List<Food> _FoodList = null;

        public BMenuIterator(BMenu macDonaldMenu)
        {
            this._FoodList = macDonaldMenu.GetFoods();
        }

        private int _CurrentIndex = -1;
        public Food Current
        {
            get
            {
                return this._FoodList[_CurrentIndex];
            }
        }
        public bool MoveNext()
        {
            return this._FoodList.Count > ++this._CurrentIndex;

        }

        public void Reset()
        {
            this._CurrentIndex = -1;
        }
    }
    /// <summary>
    /// A饭店的菜单
    /// </summary>
    public class AMenu
    {
        private Food[] _FoodList = new Food[3];

        public AMenu()
        {
            this._FoodList[0] = new Food()
            {
                Id = 1,
                Name = "汉堡包",
                Price = 15
            };
            this._FoodList[1] = new Food()
            {
                Id = 2,
                Name = "可乐",
                Price = 10
            };
            this._FoodList[2] = new Food()
            {
                Id = 3,
                Name = "薯条",
                Price = 8
            };
        }
        public Food[] GetFoods()
        {
            return this._FoodList;
        }
        public IIterator<Food> GetEnumerator()
        {
            return new AIterator(this);
        }
    }
    /// <summary>
    /// B饭店菜单
    /// </summary>
    public class BMenu
    {
        private List<Food> _FoodList = new List<Food>();

        public BMenu()
        {
            this._FoodList.Add(new Food()
            {
                Id = 1,
                Name = "鸡肉卷",
                Price = 15
            });
            this._FoodList.Add(new Food()
            {
                Id = 2,
                Name = "红豆派",
                Price = 10
            });
            this._FoodList.Add(new Food()
            {
                Id = 3,
                Name = "薯条",
                Price = 9
            });
        }

        public List<Food> GetFoods()
        {
            return this._FoodList;
        }
        public IIterator<Food> GetEnumerator()
        {
            return new BMenuIterator(this);
        }
    }

执行代码:

Console.WriteLine("********************AMenu********************");
AMenu aMenu = new AMenu();
IIterator<Food> iterator = aMenu.GetEnumerator();
while (iterator.MoveNext())
{
    Console.WriteLine("再来一次");
    Food food = iterator.Current;
    Console.WriteLine(food.Name);
}

Console.WriteLine("********************BMenu********************");
BMenu bMenu = new BMenu();
IIterator<Food> iterator1 = bMenu .GetEnumerator();
while (iterator1.MoveNext())
{
    Console.WriteLine("再来一次");
    Food food = iterator1.Current;
    Console.WriteLine(food.Name);
}

就是在每次while循环时,_CurrentIndex 记录循环次数+1,判断是否小于数组长度。

2.Yield

Yield是语法糖,系统提供便捷功能。编译时由编译器生成Iterrator的代码,包括movenext current reset
我们来通过代码说明:

 public class YieldDemo
  {
       public IEnumerable<int> Power()
       {
           for (int i = 0; i < 10; i++)
           {
               yield return this.Get(i); 
               Console.WriteLine("yield这里再来一次");
               //yield return this.Get(i) + 1;
           }
       }

       public IEnumerable<int> Common()
       {
           List<int> intList = new List<int>();
           for (int i = 0; i < 10; i++)
           {
               intList.Add(this.Get(i));
               Console.WriteLine("集合这里再来一次");
           }
           return intList;
       }

       private int Get(int num)
       {
           Thread.Sleep(2000);
           return num * DateTime.Now.Second;
       }
   }

执行:

Console.WriteLine("*******************Yield********************")
YieldDemo yieldDemo = new YieldDemo();
foreach (var item in yieldDemo.Power())
{
    Console.WriteLine(item);//按需获取,要一个拿一个
    if (item > 100)
        break;
}

Console.WriteLine("*******************************************");
foreach (var item in yieldDemo.Common())
{
    Console.WriteLine(item);//先全部获取,然后一起返还
    if (item > 100)
        break;
}

效果是yield会在底层就是生成了迭代器,每产生一条数据就会直接返回,Common的循环会在全部添加完成后一次性输出。
在这里插入图片描述

总结

如有补充或者有差异的地方,麻烦再评论下面指出,共勉!!!!!

  • 21
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值