详解c#中Array,ArrayList与List<T>的区别、共性与相互转换
作者:sogeisetsu
Array,ArrayList and List<T>
Array、ArrayList和List都是从IList派生出来的,它们都实现了IEnumerable接口
从某种意义上来说,ArrayList和List属于集合的范畴,因为他们都来自程序集System.Collections
,但是因为它们都是储存了多个变量的数据结构,并且都不是类似键值对的组合,并且没有先进先出或者先进后出的机制,故而称为数组。
我们一般称呼Array
,ArrayList
and List<T>
为数组。
Array
Array必须在定义且不初始化赋值的时候(不初始化的情况下声明数组变量除外)必须定义数组最外侧的长度。比如:
int[] vs = new int[10]; int[,] duoWei = new int[3, 4]; int[][] jiaoCuo = new int[3][]; // 该数组是由三个一维数组组成的
一维数组
定义
用类似于这种方式定义一个数组
int[] array = new int[5];
初始化赋值
用类似于这种方式初始化
int[] array1 = new int[] { 1, 3, 5, 7, 9 };
也可以进行隐式初始化
int[] array2 = { 1, 3, 5, 7, 9 }; string[] weekDays2 = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
用类似于下面这种方式先声明,再赋值
int[] array3; array3 = new int[] { 1, 3, 5, 7, 9 }; // OK //array3 = {1, 3, 5, 7, 9}; // Error
多维数组
数组可具有多个维度。多维数组的每个元素是声明时的数组所属类型的元素。比如说int[,]
的每个元素都是int类型而不是int[]类型。换种说法就是多维数组不能算做“数组组成的数组”。
定义
用类似下面这种方式声明一个二维数组的长度
int[,] array = new int[4, 2];
初始化赋值
用类似于下面的方式初始化多维数组:
// Two-dimensional array. int[,] array2D = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } }; // The same array with dimensions specified. int[,] array2Da = new int[4, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } }; // A similar array with string elements. string[,] array2Db = new string[3, 2] { { "one", "two" }, { "three", "four" }, { "five", "six" } }; // Three-dimensional array. int[,,] array3D = new int[,,] { { { 1, 2, 3 }, { 4, 5, 6 } }, { { 7, 8, 9 }, { 10, 11, 12 } } };
还可在不指定级别的情况下初始化数组
int[,] array4 = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
不初始化的情况下声明数组变量:
int[,] array5; array5 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } }; // OK //array5 = {{1,2}, {3,4}, {5,6}, {7,8}}; // Error
元素赋值和获取元素
可以用类似于array[1,2]
的方式来获取数组的值和为数组赋值。
GetLength(0)
可以获取最外围数组的长度,GetLength(1)
可以获得第二层数组的长度。以此类推。为一个二维数组duoWei
循环赋值的方式如下:
Console.WriteLine("二维数组赋值"); for (int i = 0; i < duoWei.GetLength(0); i++) { for (int j = 0; j < duoWei.GetLength(1); j++) { duoWei[i, j] = i + j; } }
如何获取二维数组中的元素个数呢?
int[,] array = new int[,] {{1,2,3},{4,5,6},{7,8,9}};//定义一个3行3列的二维数组 int row = array.Rank;//获取维数,这里指行数 int col = array.GetLength(1);//获取指定维度中的元素个数,这里也就是列数了。(0是第一维,1表示的是第二维) int col = array.GetUpperBound(0)+1;//获取指定维度的索引上限,在加上一个1就是总数,这里表示二维数组的行数 int num = array.Length;//获取整个二维数组的长度,即所有元的个数
交错数组
交错数组是一个数组,其元素是数组,大小可能不同。 交错数组有时称为“数组的数组”。
交错数组不初始化就声明的方式如下:
int[][] ccf; ccf = new int[3][];
交错数组类似于python的多维数组,比较符合人类的直觉,一个交错数组里面包含了多个数组。
定义
可以采用类似下面的方式来声明一个交错数组:
// 定义多维数组要求每个维度的长度都相同 下面定义交错数组 int[][] jiaoCuo = new int[3][]; // 该数组是由三个一维数组组成的
上面声明的数组是具有三个元素的一维数组,其中每个元素都是一维整数数组。
可使用初始化表达式通过值来填充数组元素,这种情况下不需要数组大小。 例如:
jaggedArray[0] = new int[] { 1, 3, 5, 7, 9 }; jaggedArray[1] = new int[] { 0, 2, 4, 6 }; jaggedArray[2] = new int[] { 11, 22 };
初始化赋值
可在声明数组时将其初始化,如:
int[][] jaggedArray2 = new int[][] { new int[] { 1, 3, 5, 7, 9 }, new int[] { 0, 2, 4, 6 }, new int[] { 11, 22 } };
获取元素和单个赋值
可以用类似于jiaoCuo[1][1]
来获取单个元素的值,也可以用类似于jiaoCuo[1][1] = 2;
来为单个元素赋值。
可以采取类似于下面的方式来进行循环赋值:
Console.WriteLine("交错数组循环赋值"); // 先声明交错数组中每一个数组的长度 for (int i = 0; i < 3; i++) { jiaoCuo[i] = new int[i + 1]; } // 然后对交错数组中的每一个元素赋值 for (int i = 0; i < jiaoCuo.Length; i++) { Console.WriteLine($"交错数组的第{i + 1}层"); for (int j = 0; j < jiaoCuo[i].Length; j++) { jiaoCuo[i][j] = i + j; Console.WriteLine(jiaoCuo[i][j]); } }
方法和属性
像数组这种储存多个变量的数据结构,最重要的就是增查删改、获取长度和数据类型转换。Array因为数组的特性,长度不可改变,所以增查删改只能有查和改。
改
Array类型用用类似于下面的方式进行改操作:
vs[0] = 12; //一维数组 duoWei[1, 2] = 3; //多维数组 jiaoCuo[1][1] = 2; //交错数组
查
Array类型用类似于下面的方式进行查操作:
int[] vs = new int[10]; vs[0] = 12; Console.WriteLine(Array.IndexOf(vs, 12)); //0 Console.WriteLine(vs.Contains(12)); // True
获取长度
可以用类似于下面这种方式来获取:
Console.WriteLine(vs.Length); Console.WriteLine(vs.Count());
交错数组的Length
是获取所包含数组的个数,多维数组的Length
是获取数组的元素的总个数,多维数组GetLength(0)
可以获取最外围数组的长度,GetLength(1)
可以获得第二层数组的长度。以此类推。
Array.ConvertAll() 数据类型转换
可以用Array.ConvertAll<TInput,TOutput>(TInput[], Converter<TInput,TOutput>)
来进行数组类型的转换。
参数如下:
- array
TInput[]要转换为目标类型的从零开始的一维 Array。
- converter
Converter用于将每个元素从一种类型转换为另一种类型的 Converter。
来源:[Array.ConvertAll(TInput], Converter) 方法 (System) | Microsoft Docs
demo如下:
double[] vs3 = Array.ConvertAll(vs, item => (double)item);
切片
默认状态下只能对一维数组进行切片,或者通过交错数组获取的一维数组也可以进行切片。
切片的方式类似于vs[1..5]
,表示vs数组从1到5,左闭右开。^1表示-1,即最后一个元素。[^3..^1]
表示倒数第三个元素到倒数第一个元素,左闭右开。
获取单个元素和赋值
可以采用下面的方式来获取单个元素和为单个元素单独赋值:
// 一维数组 Console.WriteLine(vs[1]); vs[1] = 2; // 多维数组 Console.WriteLine(duoWei[1, 2]); duoWei[1, 2] = 3; // 交错数组 Console.WriteLine(jiaoCuo[1][0]); jiaoCuo[1][0] = 0;
Array.ForEach 循环
System.Array里面也有ForEach方法,这是用于Array的。
demo:
Array.ForEach(vs, item => Console.WriteLine(item));
ArrayList
定义
用类似于下面的三种方式中的任意一种来声明ArrayList:
ArrayList() | 初始化 ArrayList 类的新实例,该实例为空并且具有默认初始容量。 |
---|---|
ArrayList(ICollection) | 初始化 ArrayList 类的新实例,该类包含从指定集合复制的元素,并具有与复制的元素数相同的初始容量。 |
ArrayList(Int32) | 初始化 ArrayList 类的新实例,该实例为空并且具有指定的初始容量。 |
可以将Arraylist看作是一种长度可以自由变换,可以包含不同数据类型元素的数组。
初始化赋值
可以采用类似于下面的方式来初始化赋值:
ArrayList arrayList1 = new ArrayList() { 12, 334, 3, true };
循环
循环可以用for和foreach。
foreach (var item in arrayList) { Console.WriteLine(item); }
方法和属性
和list<T>
类似,但是没有ConvertAll
方法。ArrayList本身没有ForEach方法,但是也可以用传统的foreach方法(就像前面提到的ArrayList的循环那样)。
具体的方法和属性请查看List
List<T>
定义
用类似于下面的三种方式中的任意一种来声明List<T>
:
List() | 初始化 List 类的新实例,该实例为空并且具有默认初始容量。 |
---|---|
List(IEnumerable) | 初始化 List 类的新实例,该实例包含从指定集合复制的元素并且具有足够的容量来容纳所复制的元素。 |
List(Int32) | 初始化 List 类的新实例,该实例为空并且具有指定的初始容量。 |
初始化
用类似于下面的方式在声明时初始化:
List<string> listA = new List<string>() { "hello", " ", "wrold" };
循环
List<T>
有一个名称为ForEach
的方法:
public void ForEach (Action<T> action);
该方法的本质是要对 List 的每个元素执行的 Action 委托。Action 的参数即为List<T>
在循环过程中的每个元素。
demo如下:
// 声明 List<string> listA = new List<string>() { "hello", " ", "wrold" }; // 循环 var i = 0; listA.ForEach(item => { Console.WriteLine($"第{i + 1}个"); Console.WriteLine(item); i++; });
方法和属性
从获取长度、增查删改、数据类型转换、切片和循环来解析。其中除了数据类型转换和List<T>
类型本身就拥有的ForEach方法外,都适用于ArrayList。
先声明一个List<string>
作为演示的基础:
List<string> listA = new List<string>() { "hello", " ", "wrold" };
属性 长度
Count属性可以获取长度
Console.WriteLine(listA.Count);
属性 取值
Console.WriteLine(listA[0]);
增
即增加元素,可以用Add方法:
listA.Add("12");
查
IndexOf获取所在位置,Contains获取是否包含。
Console.WriteLine(listA.IndexOf("12")); Console.WriteLine(listA.Contains("12"));
删
Remove根据数据删除,RemoveAt根据位置删除。
listA.Remove("12"); listA.RemoveAt(1);
改
可以用类似于listA[1] = "改变";
的方式来修改元素内容。
切片
可以用GetRange(int index, int count)
来进行切片操作,第一个参数是切片开始的位置,第二个参数是切片的数量,即从index开始往后数几个数。
Console.WriteLine(listA.GetRange(1, 1).Count);
循环
List<T>
有一个名称为ForEach
的方法,该方法的本质是要对 List 的每个元素执行的 Action 委托。Action 的参数即为List<T>
在循环过程中的每个元素。
demo如下:
// 声明 List<string> listA = new List<string>() { "hello", " ", "wrold" }; // 循环 var i = 0; listA.ForEach(item => { Console.WriteLine($"第{i + 1}个"); Console.WriteLine(item); i++; });
数据类型转换
可以用ConvertAll来对数组的数据类型进行转换,这是List<T>
自带的方法。System.Array里面也有ConvertAll方法,这是用于Array的。
List<object> listObject = listA.ConvertAll(s => (object)s);
区别
成员单一类型 | 长度可变 | 切片友好 | 方法丰富 | 增查删改 | ConvertAll | |
---|---|---|---|---|---|---|
一维数组 | ✔ | ❌ | ✔ | ❌ | 查、改 | ✔ |
多维数组 | ✔ | ❌ | ✔ | ❌ | 查、改 | ✔ |
交错数组 | ✔ | ❌ | ✔ | ❌ | 查、改 | ✔ |
ArrayList | ❌ | ✔ | ❌ | ✔ | 增查删改 | ❌ |
List<T> |
✔ | ✔ | ❌ | ✔ | 增查删改 | ✔ |
Array最大的好处就是切片友好,可以使用类似于[1..3]
的方式切片,这是比GetRange
更加直观的切片方式。List<T>
类型可以通过ToArray的方法来转变成Array。
Array,ArrayList and List<T>之间的转换
关于这一部分的demo代码详情可从Array,ArrayList and List之间的转换 · sogeisetsu/Solution1@88f27d6 (github.com)获得。
先分别声明这三种数据类型。
// 声明数组 int[] a = new int[] { 1,3,4,5,656,-1 }; // 声明多维数组 int[,] aD = new int[,] { { 1, 2 }, { 3, 4 } }; // 声明交错数组 int[][] aJ = new int[][] { new int[]{ 1,2,3}, new int[]{ 1} }; // 声明ArrayList ArrayList b = new ArrayList() { 1, 2, 344, "233", true }; // 声明List<T> List<int> c = new List<int>();
Array转ArrayList
// 数组转ArrayList ArrayList aToArrayList = new ArrayList(a);
Array转List<T>
List<int> aToList = new List<int>(a); List<int> aToLista = a.ToList();
List<T>转Array
int[] cToList = c.ToArray();
List<T>转ArrayList
ArrayList cToArrayList = new ArrayList(c);
ArrayList转Array
// ArrayList转Array object[] bToArray = b.ToArray();
在转换的过程中,会丢失数据类型的准确度,简单来说就是转换成的Array会变成object
这种转换的意义不大,如果转换完之后再强行用Array.ConvertAll方法来进行数据类型的转换,很有可能会出现诸如Unable to cast object of type 'System.String' to type 'System.Int32'.
的错误,这是因为ArrayList本身成员就可以不是单一类型。
数组的打印
Array的打印
对于Array的打印,我找到了四种方式,如下:
调用Array.ForEach
Array.ForEach(a, item => Console.WriteLine(item));
传统forEach
foreach (var item in a) { Console.WriteLine(item); }
传统for
for (int i = 0; i < a.Count(); i++) { Console.WriteLine(a[i]); }
string.Join
Console.WriteLine(string.Join("\t", a));
ArrayList的打印
ArrayList的打印我知道的就只有传统的for和foreach两种方式。
List<T>的打印
List<T>
的打印除了传统的for和foreach两种方式之外,还有List<T>
本身自带的foreach:
var i = 0; listA.ForEach(item => { Console.WriteLine($"第{i + 1}个"); Console.WriteLine(item); i++; });
请注意:ArrayList和List<T>
均没有string.Join
和调用Array.ForEach
两种方式来打印数组。
到此这篇关于详解c#中Array,ArrayList与List<T>的区别、共性与相互转换的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。