C#基础回顾--集合

一、什么是集合

集合:存放在某一个容器中的一组数据。集合的类型主要有数组、动态数组(ArrayList),泛型列表(List),哈希表(HashTable),字典(Dictionary),队列(queue),栈(stack)
不同的集合类型对应的数据存储方式,操作方式是不一样的。数组用于存放固定大小的数据集合;ArrayList则用于存储不同类型的数据(较少使用);List则用于存储同一类型的数组,优势在于利于检索(特别是结合linq使用时,更加的简洁方便);哈希表和字典则是一种键值对的数据集合,对键值对的操作性能是非常优越的;队列则是一种先进先出的数据结构,使用队列我们可以很好的处理需要顺序执行的数据;栈则是后进先出的数据结构,

数组

数组是存储固定大小的数据集合。可以是一维数组,多维数组。

  1. 固定大小(也就是固定长度,不可动态改变长度)。数组初始化时指定大小。
  2. 数据量一般比较小,整体所占空间一般也比较小

在存储数据量较小,并且不需改变的时使用比较多。

            string[] weekdays = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" };//直接初赋值
            int[] amounts = new int[10];   //限定数组长度,int类型则默认为0

            string wednesday = weekdays[2]; //通过下标索引获取对应的值
            int weekdaysLength = weekdays.Length;   //数组的Length属性可得到当前数组的长度

            //使用迭代器遍历,正常情况下都是使用for或者foreach进行遍历,这里只是示例
            var enumerator = amounts.GetEnumerator();
            while (enumerator.MoveNext())
            {
                Console.WriteLine(enumerator.Current.ToString());
            }

            int[,] sizes = new int[4, 3];   //声明二维数组4*3  
            int[,] packageSizes = new int[,] { { 5,5,6},{3,4,5 } };//声明二维数组并直接初赋值        
View Code

 

ArrayList

ArrayList是线性结构、非泛型集合(动态数组)。

  1. 它可以自动改变大小,数据分布于连续的内存空间中。由于其内存分布的连续性,所以对于频繁的操作增、删元素的性能也是比较差的(后续元素需要偏移,当新增元素导致集合超出长度时,怎么需要拷贝到新的数组集合中)。
  2. 它仅支持object类型的数据。因为所有类型都继承与object,所以相当于可以任意存储各类类型数据。如果是值类型数据则会涉及装箱/拆箱操作,数据量大时影响性能则尤为明显。
  3. 已有封装好的增、删、获取元素等接口,很方便开发使用。

这类集合还是比较少用的,基本上都是使用对应的泛型集合(List)。

ArrayList tmpArrayList = new ArrayList();
            //添加元素,不限制数据类型,继承object的都可以.值类型数据默认会进行装箱操作
            tmpArrayList.Add("多线程");
            tmpArrayList.Add(100);  
            tmpArrayList.AddRange(new int[] { 89, 92, 86,100,100 });
            //通过下标获取数据
            var firstElement = tmpArrayList[0];
            //获取指定连续的数据
            ArrayList newArray= tmpArrayList.GetRange(1,4);
            int length = tmpArrayList.Count; //获取动态数组的总的元素个数
            //删除操作
            tmpArrayList.RemoveAt(3);
            tmpArrayList.Remove(100);//移除第一个匹配的项
            foreach (var item in tmpArrayList)
            {
                Console.WriteLine(item.ToString());
            }
            //tmpArrayList.Sort(); //排序,此处会报错,因为第一个为字符串类型,不支持排序
            tmpArrayList.Reverse();//反转
            tmpArrayList.Clear();//清空
View Code

 

List

List是线性结构的泛型集合。

  1. 它继承了ArrayList的特点,已有封装好的增、删、获取元素等接口,很方便开发使用。
  2. 支持指定的泛型类型的数据,例如List<int>只支持int类型的数据。因为有了泛型的约束,所以就减少了装箱/拆箱的操作。
  3. 可自动改变大小,分布于连续的内存空间中。频繁操作添加元素、删除元素操作的性能还是比较差的。因为添加或者删除元素时可能需要对已有元素进行偏移操作。甚至可能需要重建新的集合进行存放(当新增元素导致超出集合最大长度时,会重新创建一个长度是当前数据2倍的空间的集合)。

这是我们开发中经常用到的一种集合。存储没有太多增删操作的无序列表(一般都是用于临时存储,用于数据检索过滤的,我们还经常会用到的是list基于linq的查询)。

LinkedList

LinkedList是线性结构双向链表。

  1. 它的内存分布可以是不连续的。所以增、删除元素时只需修改前驱和后续而无需偏移其他元素
  2. 由于是双向链表,很容易检索到前驱与后续
  3. 其所占的空间比对应的同等数据的list要大很多,因为他除了元素数据外,每个元素还要记录前驱后续

较少使用,主要用于频繁增、元素的情况下。

以下是简单实现一个链表,仅作参考,如需详细了解net的链表实现还请查看其源码

    public class CustomerLinkedNode<T>
    {
        /// <summary>
        /// 当前值
        /// </summary>
        public T Value { get; set; }
        /// <summary>
        /// 前驱
        /// </summary>
        public CustomerLinkedNode<T> Privious { get; set; }
        /// <summary>
        /// 后续
        /// </summary>
        public CustomerLinkedNode<T> Next { get; set; }

    }
    public class CustomerLinkedList<T>
    {
        private int count = 0;
        public int Count { get { return count; } }
        private CustomerLinkedNode<T> first;
        /// <summary>
        /// 首项值
        /// </summary>
        public T First
        {
            get
            {
                if (first == null)
                {
                    return default(T);
                }
                else
                {
                    return first.Value;
                }
            }
        }
        private CustomerLinkedNode<T> last;
        /// <summary>
        /// 尾项值
        /// </summary>
        public T Last
        {
            get
            {
                if (last == null)
                {
                    return default(T);
                }
                else
                {
                    return last.Value;
                }
            }
        }
        /// <summary>
        /// 添加为首项
        /// </summary>
        /// <param name="item"></param>
        public void AddFirst(T item)
        {
            if (first == null)
            {
                first = new CustomerLinkedNode<T>()
                {
                    Value = item,
                    Next = null,
                    Privious = null,
                };
                last = first;
            }
            else
            {
                var newFirst = new CustomerLinkedNode<T>()
                {
                    Value = item,
                    Next = first,
                    Privious = null,
                };
                first.Privious = newFirst;
                first = newFirst;
            }
            count++;
        }
        /// <summary>
        /// 添加为尾项
        /// </summary>
        /// <param name="item"></param>
        public void AddLast(T item)
        {
            if (first == null)
            {
                //链表为空时,first=last
                first = new CustomerLinkedNode<T>()
                {
                    Value = item,
                    Next = null,
                    Privious = null,
                };
                last = first;
            }
            else
            {
                var newLast = new CustomerLinkedNode<T>()
                {
                    Value = item,
                    Next = null,
                    Privious = last,
                };
                last.Next = newLast;
                last = newLast;
            }
            count++;
        }
        /// <summary>
        /// 指定索引前部添加
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        public void AddBefore(int index, T item)
        {
            if (index < 0 || index > count - 1)
            {
                throw new Exception("索引超出");
            }
            if (index == 0)
            {
                AddFirst(item);
                return;
            }
            var newNode = new CustomerLinkedNode<T>
            {
                Value = item,
            };

            int tmpIndex = 0;
            for (var node = first; node != null; node = node.Next)
            {
                if (tmpIndex == index)
                {
                    node.Privious.Next = newNode;
                    newNode.Privious = node.Privious;
                    newNode.Next = node;
                    node.Privious = newNode;
                    count++;
                    break;
                }
                tmpIndex++;
            }
        }
        /// <summary>
        /// 指定索引后部添加
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        public void AddAfter(int index, T item)
        {
            if (index < 0 || index > count - 1)
            {
                throw new Exception("索引超出");
            }
            if (index == count - 1)
            {
                AddLast(item);
                return;
            }
            var newNode = new CustomerLinkedNode<T>
            {
                Value = item,
            };

            int tmpIndex = 0;
            for (var node = first; node != null; node = node.Next)
            {
                if (tmpIndex == index)
                {
                    newNode.Privious = node;
                    newNode.Next = node.Next;
                    node.Next.Privious = newNode;
                    node.Next = newNode;
                    count++;
                    break;
                }
                tmpIndex++;
            }
        }

        //引用类型不能简单的使用==进行判断是否是同一对象,暂不实现
        ///// <summary>
        ///// 移除第一个匹配的项
        ///// </summary>
        ///// <param name="t"></param>
        //public bool Remove(T t)
        //{
        //    for (var node = first; node != null; node = node.Next)
        //    {
        //        if (node.Value == t)
        //        { 

        //        }
        //    }
        //}
        /// <summary>
        /// 移除指定索引项
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool RemoveAt(int index)
        {
            if (index < 0 || index > count - 1)
            {
                throw new Exception("索引超出");
            }
            int tmpIndex = 0;
            for (var node = first; node != null; node = node.Next)
            {
                if (tmpIndex == index)
                {
                    if (node.Privious != null)
                    {
                        node.Privious.Next = node.Next;
                    }
                    if (node.Next != null)
                    {
                        node.Next.Privious = node.Privious;
                    }

                    count--;
                    break;
                }
                tmpIndex++;
            }
            return true;
        }
        /// <summary>
        /// 清空
        /// </summary>
        public void Clear()
        {
            this.count = 0;
            first = null;
            last = null;
        }
    }
View Code

 

ConcurrentBag

ConcurrentBag是线程安全的线性结构的泛型集合。

内部实现可参照https://www.cnblogs.com/InCerry/p/9497729.html

主要用于多线程操作列表的情况下

 

HashTable

在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于存储、处理key-value的键值对。

  1. 因为是键值对的关系,通过key可用来快速获取到value。是一种非常快速高效的数据结构。很多缓存框架都是使用类似于这种的键值对进行存储数据。
  2. Hashtable中keyvalue键值对均为object类型,所以Hashtable可以支持任何类型的key-value键值对。所以在处理值类型数据时,会涉及装/拆箱操作

  3. 键必须唯一

在开发过程中,.net的hashtable其实用的不是很多。我们更多的是使用支持泛型结构的Dictionary

简单的代码示例,无意义

            var tmpHashtable = new Hashtable();
            tmpHashtable.Add("广州","GZ");
            tmpHashtable.Add("北京", "BJ");
            tmpHashtable.Add("深圳","SZ");
            tmpHashtable.Add("上海","SH");
            var szCode = tmpHashtable["深圳"];  //获取指定键的值
            //遍历
            foreach (DictionaryEntry item in tmpHashtable)
            {
                Console.WriteLine(item.Value.ToString());
            }
            if (tmpHashtable.ContainsKey("北京"))
            { 
                //TODO something
            }
            tmpHashtable.Remove("广州");   //移除
            //...
View Code

 

Dictionary

dictionay(字典)是支持泛型的键值对集合

  1. Dictionary跟hashtable的结构是一样的,键值对数据结构,键唯一。
  2. 泛型约束,所以其在声明时已经确定的数据类型。对于值类型数据,其效率高于hashtable(少了装箱,但多了类型校验);

ConcurrentDictionary

ConcurrentDictionary 线程安全的数据字典结构。(很少使用,略)

 

Queue

队列是先进先出的数据集合。

  1. 结构规则是先进先出,元素的增加(入队)只能是在队尾添加,使用方法Enqueue;删除(出队)必须是从队首操作,使用方法Dequeue或者TryDequeue
  2. 支持泛型约束。
 Queue<SaleOrder> orderQueue = new Queue<SaleOrder>();
            //入队
            orderQueue.Enqueue(new SaleOrder()
            {
                Id = Guid.NewGuid(),
                OrderDate=new DateTime(2020,11,11),
                OrderNumber="A00008901",
            }) ;
            orderQueue.Enqueue(new SaleOrder()
            {
                Id = Guid.NewGuid(),
                OrderDate = new DateTime(2020, 11, 11),
                OrderNumber = "A00008902",
            });
            orderQueue.Enqueue(new SaleOrder()
            {
                Id = Guid.NewGuid(),
                OrderDate = new DateTime(2020, 12, 1),
                OrderNumber = "A00008903",
            });
            //遍历队列
            foreach (var item in orderQueue)
            {
                Console.WriteLine(item);
            }
            var order=orderQueue.Dequeue();//出队(获取值并删除),也可以使用TryDequeue方法 
            Console.WriteLine(order.OrderNumber);
            var secondOrder=orderQueue.Peek();//获取队首元素,不删除;也可以使用TryPeek方法
            Console.WriteLine(secondOrder.OrderNumber);
View Code

 

ConcurrentQueue

ConcurrentQueue是线程安全的队列(很少使用,略)

 

Stack

Stack(栈)是一种先进后出的数据结构。

  1.  结构规则是先进后出,所有的操作都是在栈顶进行操作的。
  2. 支持泛型

 

 Stack<char> stack = new Stack<char>();
           //压入栈
            stack.Push('(');
            stack.Push('1');
            stack.Push('+');
            stack.Push('8');
            stack.Push(')');
            stack.Push('*');
            stack.Push('5');
            StringBuilder expressString = new StringBuilder();
            //遍历栈元素
            foreach (var item in stack)
            {
                expressString.Append(item.ToString());     
                    }
            Console.WriteLine(expressString); 

            var val1 = stack.Pop();//弹出栈(同时删除该数据),也可使用TryPop方法
            Console.WriteLine(val1.ToString());
            var val2 = stack.Peek();  //获取栈顶数据,不删除。也可使用TryPeek方法
            Console.WriteLine(val2.ToString());
View Code

 

 

ConcurrentStack

ConcurrentStack线程安全的栈。(用的比较少,略)

 

 

posted on 2020-12-03 20:52  john_yong  阅读(178)  评论(0编辑  收藏  举报

导航