C#中常用容器的使用与底层数据结构,特点(1)‘线’性集合

主要是数组:ArrayList,List<T>,以及LinkList<T>

重点先排除ArrayList官方都不推荐,涉及装箱拆箱,几乎没什么优点,

有图有真相,插入200_000量级数据测试结果,

令人意外的是LinkedList删除,增加效果运行挺快的,

至于list移除和insert相对来说效率比较低

关于使用方面:

数组Array,ArrayList,List<>,都是通过下标索引访问,遍历for和foreach都可以,

ArrayList 几乎等于List<object,object>

而LinkedList没有继承Ilist集合,只能通过First(),Last()访问第一个,或者最后一个

然后是foreach迭代遍历

备注:

关于List<>和LinkedList<>区别,主要是在给GC(垃圾回收机制)的压力上,

一个List<T>本质上是将其数据保存在一个堆栈的数组上,而LinkedList<T>是将其所有节点保存在堆栈上(人家是一个,我是一系列)。这就使得GC需要更多地管理堆栈上LinkedList<T>的节点对象。注意,List<T>.Insert*方法比在LinkedList<T>中使用Add*方法在任何地方添加一个节点可能要慢。然而,这个依赖于List<T>插入对象的位置。Insert方法必须使所有在插入点后面的元素往后移动一位。如果新元素被插在List<T>最后或接近最后的位置,那么相对于GC维护LinkedList<T>节点的总的开销来说,其开销是可以被忽略的。

测试,以及具体差别说明,注释如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ArrDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            TestArr();
            TestArrayList();
            TestList();
            TestLinkList();
            Console.ReadKey();



        }

        public static void TestArr()
        {
            //第一个:数组:
            //优点:是快,在内存中是连续存储的,所以它的索引速度非常快,
            //缺点:死板,和其他类似集合相比较,数组特点容量固定,且一次只能设置或获取一个元素的值,
            //常用用法:
            string[] s = new string[2];
            //赋值 
            s[0] = "a";
            s[1] = "b";
            //修改 
            s[1] = "a1";
            //删除没有意义,指定长度,内存大小就分配好了
            //访问数据
            string test = s[2];
            //遍历很简单
            //测试性能
            int[] t = new int[200_000];
            Stopwatch sw = new Stopwatch();
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                t[i] = i;
            }
            Console.WriteLine($"t[i]=i耗时:{sw.ElapsedMilliseconds}ms");
            //

        }

        public static void TestArrayList()
        {
            //第二个:ArrayList:
            //ArrayList是命名空间System.Collections下的一部分,在使用该类时必须进行引用,同时继承了IList接口,提供了数据存储和检索。
            //ArrayList对象的大小是按照其中存储的数据来动态扩充与收缩的。所以,在声明ArrayList对象时并不需要指定它的长度。
            //继承接口:public class ArrayList : ICloneable, System.Collections.IList
            这个建议不用看,节约时间,建官方
            /*https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.arraylist?view=netcore-3.1
             *  重要
建议不要将 ArrayList 类用于新的开发。 相反,我们建议使用泛型 List<T> 类。
ArrayList 类旨在保存对象的异类集合。 但是,它并不总是提供最佳性能。 相反,我们建议执行以下操作:
对于异类对象集合,请使用 List<Object> (在中C#)或 List(Of Object) (Visual Basic)类型。
对于同类对象的集合,请使用 List<T> 类。
             */
            ArrayList list1 = new ArrayList();
            //新增数据 
            list1.Add("cde");
            list1.Add(5678);
            //修改数据 
            list1[2] = 34;
            //移除数据 
            list1.RemoveAt(0);
            //插入数据 
            //访问数据
            object test = list1[2];
            //遍历很简单
            //
            //底层数据结构就是数组。类似于C++里面没有泛型的Vector。
            //
            //测试性能
            ArrayList arrayList = new ArrayList();
            Stopwatch sw = new Stopwatch();
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                arrayList.Add(i);
            }
            Console.WriteLine($"arrayList.Add(i)耗时:{sw.ElapsedMilliseconds}ms");
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                arrayList.RemoveAt(0);
            }
            Console.WriteLine($"arrayList.RemoveAt(0)耗时:{sw.ElapsedMilliseconds}ms");
            //arr
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                arrayList.Insert(0, i);
            }
            Console.WriteLine($"arrayList.Insert(0,i)耗时:{sw.ElapsedMilliseconds}ms");
        }

        public static void TestList()
        {
            //第三个:List<list>
            List<string> list = new List<string>();

            //不能放在1处
            list.Insert(0, "2");
            //新增数据 
            list.Add("123");
            //不能插在2的位置,有一个元素,只能插在前面后面
            list.Insert(1, "789");
            //修改数据 
            list[0] = "456";
            //访问数据
            string test = list[2];
            //遍历很简单

            //移除数据 
            //list.RemoveAt(0);
            //必须接受一个断言委托,即返回一个bool类型,参数位元素item
            //T也许不一定是简单类型,可能是复杂类型
            string rest = list.Find(item => item == "789");

            //测试一下,增加元素,是否改变原来索引
            //list.Add(1);//类型安全的
            //一开始就错了,不能在index为3处插入值,
            //0,0,789 原本是123,0,0,789,第一个改变,又删除了
            //456,789,123
            //456,234,789,123,
            list.Insert(1, "234");
            //增加默认是从第几个开始呢?0,234,789,
            list.Add("test");

            int index = list.IndexOf("2");
            int index2 = list.IndexOf("test");
            //一系列相同类型元素,毫无疑问用list绝对方便
            //每次插入新的元素,都会调整插入位置之后元素索引
            list.Remove("test");
            list.RemoveAt(2);
            //456,234,123
            list.Sort();
            //可能需要每次调整索引,尤其是从前面调整数据,后面索引全部跟着变化

            //测试性能
            List<int> lst = new List<int>();
            Stopwatch sw = new Stopwatch();
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                lst.Add(i);
            }
            Console.WriteLine($"list.Add(i)耗时:{sw.ElapsedMilliseconds}ms");
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                lst.RemoveAt(0);
            }
            Console.WriteLine($"list.RemoveAt(0)耗时:{sw.ElapsedMilliseconds}ms");
            //arr
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                lst.Insert(0, i);
            }
            Console.WriteLine($"list.Insert(0,i)耗时:{sw.ElapsedMilliseconds}ms");
        }

        //
        public static void TestLinkList()
        {
            LinkedList<string> list = new LinkedList<string>();
            LinkedListNode<string> node1 = list.AddFirst("Data Value 1");
            LinkedListNode<string> node2 = list.AddLast("Data Value 6");
            list.AddBefore(node1, new LinkedListNode<string>("测试"));
            list.RemoveFirst();
            LinkedListNode<string> resnode = list.Find("测试");
            //访问数据
            string test = list.First();
            //遍历,通过迭代器foreach方式,
            foreach (string item in list)
            {

            }

            LinkedList<int> lst = new LinkedList<int>();
            Stopwatch sw = new Stopwatch();
            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                lst.AddFirst(i);
            }
            //lst[0] = 56;
            Console.WriteLine($"LinkedList.AddFirst((i)耗时:{sw.ElapsedMilliseconds}ms");

            sw.Restart();
            for (int i = 0; i < 200_000; i++)
            {
                lst.RemoveFirst();
            }
            //lst[0] = 56;
            Console.WriteLine($"LinkedList.RemoveFirst((i)耗时:{sw.ElapsedMilliseconds}ms");

        }
        public static void StudyArrayByDoc()
        {
            /*
           数组具有以下属性:
数组可以是一维、多维或交错的。
创建数组实例时,将建立纬度数量和每个纬度的长度。 这些值在实例的生存期内无法更改。
数值数组元素的默认值设置为零,而引用元素设置为 null。
交错数组是数组的数组,因此其元素为引用类型且被初始化为 null。
数组从零开始编制索引:包含 n 元素的数组从 0 索引到 n-1。
数组元素可以是任何类型,其中包括数组类型。
数组类型是从抽象的基类型 Array 派生的引用类型。 由于此类型实现 IEnumerable 和 IEnumerable<T>,因此可以在 C# 中的所有数组上使用 foreach 迭代。
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/arrays/

          可以将同一类型的多个变量存储在一个数组数据结构中。
          通过指定数组的元素类型来声明数组。 
          如果希望数组存储任意类型的元素,可将其类型指定为 object。
          在 C# 的统一类型系统中,所有类型(预定义类型、用户定义类型、引用类型和值类型)都是直接或间接从 Object 继承的。
           */

            //总结是,第一:自定义有三种:
            // Declare a single-dimensional array of 5 integers.
            int[] array1 = new int[5];

            // Declare and set array element values.
            int[] array2 = new int[] { 1, 3, 5, 7, 9 };

            // Alternative syntax.
            int[] array3 = { 1, 2, 3, 4, 5, 6 };

            //第二:二维数组,两种定义方式
            // Declare a two dimensional array.
            int[,] multiDimensionalArray1 = new int[2, 3];

            // Declare and set array element values.
            int[,] multiDimensionalArray2 = { { 1, 2, 3 }, { 4, 5, 6 } };


            //第三:关于交错数组,等等暂时不做讨论,学习
            // Declare a jagged array.
            int[][] jaggedArray = new int[6][];
            //
            // Set the values of the first array in the jagged array structure.
            jaggedArray[0] = new int[4] { 1, 2, 3, 4 };

            //https://docs.microsoft.com/zh-cn/dotnet/api/system.array?view=netcore-3.1
            //提供一些方法,用于创建、处理、搜索数组并对数组进行排序,从而充当公共语言运行时中所有数组的基类。

            int[] iArr = new int[10];
            int[] iArr2 = new int[] { 1, 2 };
            int[] iArr1 = { 1, 2 };

            //
            int len = iArr.Length;//属性来自父类Array
            iArr2.CopyTo(iArr, 2);//同样来自Array
            Array.Copy(iArr1, iArr, 2);//总共四个方法,
            iArr.Clone();
            Console.WriteLine("Hello World!");
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值