主要是数组: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!");
}
}
}