C#教程 - 数组类型(Array Type)
更新记录
转载请注明出处。
2022年9月11日 发布。
2022年9月10日 从笔记迁移到博客。
说明
数组是最为常见的一种数据结构
数组是相同类型的、用一个标识符封装到一起的基本类型数据序列或对象序列
可以用一个统一的数组名和下标来唯一确定数组中的元素
实质上数组是一个线性序列,因此数组访问起来很快
数组本质上是用一个变量名称表示一组同类型的数据元素集合
每个元素通过变量名称和一个或多个方括号中的索引来访问
数组名[索引]
数组名[索引,索引]
数组名[索引][索引]
重要概念
元素:数组中每个独立的数据元素,数组中的元素类型必须相同或继承自同一个类型
秩/维度:数组可有任何正数的维度数,数组的维度也称为秩(rank)
维度长度:数组每个维度有一个长度,就是这个方向位置个数
数组长度:数组中所有维度中所有元素的总和称为数组的长度
注意:
不给数组赋值,编译器会初始化元素为该类型的默认值
交错数组的定义不能定义最后一个秩的大小
一维数组、矩阵数组的长度是固定的,交错数组的长度不是固定的
数组的下标从0开始
数组的类型
数组分为:一维数组 和 多维数组
一维数组可以认为是单行元素或元素向量
多维数组由主向量中的位置组成的,每一个位置本身又是一个数组
多维数组分类:矩阵数组 和 交错数组
矩阵数组:
数组每个维度的长度都是相同的
不管有多少个维度都是用一个[]
pandaArray[4,6,1]; //只有单个大括号
交错数组:
每一个子数组都是独立数组
子数组长度可以不同
使用多个[]
pandaArray[2][4][9]; //3组方括号
数组本质是对象
数组本质是对象,内部继承自System.Array
数组继承了许多有用的成员:
详细数组方法请看:数组笔记 和 官方文档
数组类型的结构在内存中示意图:
数组类型是引用类型,但是数组类型的元素可以是值类型也可以是引用类型
如果存储的元素都是值类型,数组被称为 值类型数组
如果存储的元素都是引用类型,数组被称为 引用类型数组
声明和实例一维数组和矩阵数组(Rectangular Array)
声明一维数组或矩阵数组
int[] arr1; //数组类型:一维整型数组
int[,] arr2; //数组类型:二维整型数组
int[,,] arr3; //数组类型:三维整型数组
long[,,,] arr4; //数组类型:四维long数组
注意:
只能用一个[]方括号,有多少维度就添n-1个,逗号
数组声明后数组的秩就确定了,但长度可以在实例的时候再添加
实例化一维数组:
int[] arr2 = new int[4];
PandaClass[] arr3 = new PandaClass[4];
实例化矩阵数组:
int[,,] arr = new int[3,5,2];
说明:使用数组创建表达式实例化数组
声明和实例化一维数组和矩阵数组内存示意图:
声明和实例交错数组(Jagged Array)
声明交错数组:
int[][] SomeArr1; //声明数组,秩等于2
int[][][] SomeArr2; //声明数组,秩等于3
示意图:
实例化交错数组:
int[][] arr = new int[3][]; //声明并实例化交错数组
arr[0] = new int[6] {1,2,3,4,5,6 }; //实例化子数组
arr[1] = new int[2] {66,666 }; //实例化子数组
arr[2] = new int[3] { 3,33,333 }; //实例化子数组
示意图:
声明和实例化混合数组
混合数组:交错数组 和 矩阵数组 的混合体
注意:混合数组只可以是矩阵数组下的交错数组
int[][,] arr = new int[3][,]; //声明并实例化一个混合数组
arr[0] = new int[1, 3]; //实例化子混合数组
arr[1] = new int[3, 4]; //实例化子混合数组
arr[2] = new int[3, 1]; //实例化子混合数组
内存结构示意图:
比较交错数组和矩阵数组
初始化数组
说明
一定要初始化数组后,再使用数组
在使用数组之前,一定要判断一下是否为null
默认初始化
如果声明和实例化数组后没有初始化,编译器会进行默认初始化
显式初始化一维数组
在实例化语句后添加一个初始化列表即可:
int[] pandaArray = new int[] { 10, 20, 30, 40 };
内存结构示意图:
显式初始化矩阵数组
int[,] pandaArray = new int[,] { { 1, 2 }, { 3, 4 }, { 4, 5 } };
内存结构示意图:
注意:
每一个初始值必须放在{}大括号内
每一个维度必须使用{}大括号并嵌套在外大括号内
使用,逗号将初始化值和{}大括号分隔开
初始化快捷语法
省略数组创建表达式,直接给数组赋值
int[] pandaArray2 = { 1, 2, 3 };
//等价于
int[] pandaArray1 = new int[3] { 1, 2, 3 };
实例二:
int[,] arr2 = { { 1, 2 }, { 3, 4 } };
//等价于
int[,] arr1 = new int[2,2] { { 1, 2 }, { 3, 4 } };
隐式类型数组
数组初始化时,编译器自动推断数组的类型
var arr1 = new[] { 1, 2, 3, 4 };
//等价于
int[] arr2 = new int[] { 1, 2, 3, 4 };
实例二:
var arr1 = new[,] { { 1, 2 }, { 3, 4 } };
//等价于
int[,] arr2 = new int[2, 2] { { 1, 2 }, { 3, 4 } };
数组的反序索引
反序索引以开头,1表示倒数第1个元素索引,^2表示倒数第二个,以此类推
适合于处理需要使用正反序处理的数值场景
注意:没有^0
注意:Span
注意:反序索引从C# 8开始支持
实例:
char[] vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };
char lastElement = vowels[^1]; // 'u'
char secondToLast = vowels[^2]; // 'o'
在C# 8中还定义了索引类型Index,可以给其复制^N值
实例:
Index first = 0;
Index last = ^1;
char firstElement = vowels [first]; // 'a'
char lastElement = vowels [last]; // 'u'
数组范围匹配(Ranges)
范围匹配类似于切片(slice),使用..运算符表示
适合于处理数组中部分元素的场景
注意:范围匹配从C#8开始支持
语法:
vowels [起始位置..结束位置]
注意:起始位置 和 结束位置 都是可选的
注意:是包含起始位置的元素,不包含结束位置的元素
实例:
char[] vowels = new char[] {'a','e','i','o','u'};
char[] firstTwo = vowels [..2]; // 'a', 'e'
char[] lastThree = vowels [2..]; // 'i', 'o', 'u'
char[] middleOne = vowels [2..3] // 'i'
char[] lastTwo = vowels [^2..^0]; // 'o', 'u'
实例:
char[] panda = { 'p', 'a', 'n', 'd', 'a' };
Console.WriteLine(panda[..]); //panda
Console.WriteLine(panda[1..]); //anda
Console.WriteLine(panda[2..]); //nda
Console.WriteLine(panda[2..3]); //n
Console.WriteLine(panda[^1..]); //a
Console.WriteLine(panda[^2..]); //da
为了支持范围匹配,C#8还添加了Range类型
Range firstTwoRange = 0..2;
char[] firstTwo = vowels [firstTwoRange]; // 'a', 'e'
使用数组
一维数组和矩阵数组
int[,] arr = new int[10,10];
Console.WriteLine(arr[0, 0]);
交错数组
int[][] arr = new int[10][];
arr[0] = new int[10];
Console.WriteLine(arr[0][0]);
数组的协变
说明
某个数据的类型不是数组的基类型,我们可以将这个数据赋值给数组的元素,这种情况叫做数组的协变(array covariance)
发生条件
数组的元素是引用类型(即数组是引用类型数组)
数组元素类型和赋值的类型间存在隐式或者显式转换
注意:
值类型数组没有协变情况
实例
using System;
namespace Test
{
class A { }
class B : A { }
class Program
{
static void Main()
{
A[] arr1 = new A[3]; //声明并实例化A类型的数组
A[] arr2 = new A[3]; //声明并实例化A类型的数组
//给数组arr1赋值
arr1[0] = new A();
arr1[1] = new A();
arr1[2] = new A();
//给数组arr2赋值(协变)
arr2[0] = new B(); //注意:这里赋值的B类型实例
arr2[1] = new B();
arr2[2] = new B();
}
}
}
示意图:
数组总结图
值类型数组和引用类型数组性能
值类型数组将在数组实例化时,立即分配其元素的内存
引用类型数组将在数组实例化时,仅分配其元素的空引用
注意:大长度的值类型数组可能导致性能问题
边界检查
如果索引超出设定的索引长度,将引发IndexOutOfRangeException异常
在访问数组索引前,一定要判断索引是否在数组的长度以内
在C# 8版本及以上,可以考虑使用^1索引
数组长度
Length returns the total number of elements in an array.
Therefore, if you had a multidimensional array such as bool cells[,,] of size 2 × 3 × 3
Length would return the total number of elements, 18
For a jagged array, Length returns the number of elements in the first array
数组实例
使用GetLength()遍历数组实例
//定义一个二维数组
int[][] pandaArray = new int[2][];
//初始化二维数组
pandaArray[0] = new int[2] { 1, 2 };
pandaArray[1] = new int[3] { 4, 5, 6 };
//使用GetLength配合for遍历数组
for (int i = 0; i < pandaArray.GetLength(0); i++)
{
for (int i2 = 0; i2 < pandaArray[i].GetLength(0); i2++)
{
Console.WriteLine(pandaArray[i][i2]);
}
}
使用foreach遍历数组实例
int[][] pandaArray = new int[2][];
pandaArray[0] = new int[2] { 1, 2 };
pandaArray[1] = new int[3] { 4, 5, 6 };
foreach (var item in pandaArray)
{
foreach (var item2 in item)
{
Console.WriteLine(item2);
}
}
超级混合数组实例
using System;
namespace PandaNamespace
{
class PandaClass
{
static void Main(string[] args)
{
//定义一级数组
int[][][] panda1 = new int[3][][];
for (int i = 0; i < panda1.Length; i++)
{
//定义二级数组
panda1[i] = new int[3][];
for (int i2 = 0; i2 < 3; i2++)
{
//定义三级数组
panda1[i][i2] = new int[3] { 1+i,2 + i, 3 + i };
}
}
for (int i = 0; i < panda1.Length; i++)
{
for (int i2 = 0; i2 < 3; i2++)
{
for (int i3 = 0; i3 < panda1[i][i2].Length; i3++)
{
Console.WriteLine(panda1[i][i2][i3]);
}
}
}
}
}
}
动态创建数组
使用Array.CreateInstance静态方法
//动态创建一位数组
int[] arr1 = (int[])Array.CreateInstance(typeof(int), 666);
//动态创建二维数组(矩阵数组)
int[,] arr2 = (int[,])Array.CreateInstance(typeof(int), 10, 10);
//动态创建二位数组(矩阵数组)
int[] firstDem = new int[] { 3,2};
int[,] arr3 = (int[,])Array.CreateInstance(typeof(int), firstDem);
//动态创建二维数组(稀疏数值)
int[][] arr4 = (int[][])Array.CreateInstance(typeof(int[]), 10);
arr4[0] = new int[] { 1, 2, 3, 4 };
arr4[1] = new int[] { 2, 3, 4, 5 };
arr4[2] = new int[] { 4, 5, 6, 7 };
获得和设置动态数组的元素
Array arr = Array.CreateInstance(typeof(int), 10);
//设置数组的元素的值
arr.SetValue(10, 0);
//获得数组元素的值
object v = arr.GetValue(0);
数组排序
int[] arr = new int[]{
3,2,4,6,1,2
};
//排序
Array.Sort(arr);
数组反序
int[] arr = new int[]{
3,2,4,6,1,2
};
//排序
Array.Sort(arr);
//反序
Array.Reverse(arr);
数组二分查找
int[] arr = new int[]{
3,2,4,6,1,2
};
//排序
Array.Sort(arr);
//二分查找
int position = Array.BinarySearch(arr, 2);
Console.WriteLine(position);
数组复制
int[] arr1 = new int[] { 1, 2, 3, 4 };
//动态创建数组
int[] arr2 = (int[])Array.CreateInstance(typeof(int), arr1.Length);
//复制数组
Array.Copy(arr1, arr2, arr1.Length);
本文来自博客园,作者:重庆熊猫,转载请注明原文链接:https://www.cnblogs.com/cqpanda/p/16676995.html