目录
● 数组实际上是由一个变量名称表示的一组同类型的数据元素,是一组相同类型的集合。 我们首先了解一下数组中的一些重要定义开始:
秩/ 维度 : 数组可以有任何为正数的维度数, 数组的维度称作秩。 意思就是说,该数组是几维数组 ,[ ] 表示一维数组, [ , ] 表示二维数组。
维度长度: 数组的每一个维度的长度, 就是这个方向的位置的元素个数.
数组长度: 数组的所有维度中的元素的总和称为数组的长度。
● 注意点:
数组一旦创建并且初始化,大小就固定了,C# 不支持动态数组。
数组的索引从0开始。那么数组中的元素个数的索引范围是 从0 到 n-1
数组的类型
● C#中有两种类型的数组,分别为一维数组和多维数组,多维数组中还分矩形数组和 交错数组(也称为 锯齿数组)。
一维数组: 指的是一组地址连续的相同类型的单行元素序列。
多维数组:有多个秩,其中每一行的元素个数都相同。 不管多少维度,总是使用一个方括号,例如:
doule[,] myDouble = new double[3,4];
交错数组(也称为 锯齿数组):有多个秩,其中每一行的元素个数不一定都相同。然后呢,为数组的每一个维度使用一对方括号:
int[][] myInt = new int[3][] { new int[] { 1, 2, 3 }, new int[] { 1 }, new int[] { 1, 2 } }; //正确 该数组就是: 1 2 3 // 第一行 1 // 第二行 1 2
数组是对象
● 数组的实例是从 System.Array 继承的对象。那么数组是引用类型, 数组的引用在栈上或者堆上, 数组的元素总是在堆上。如图:
● 数组虽然是引用类型, 但是数组的元素 可以是值类型, 也可以是引用类型(比如类)。
如果存储的元素都是值类型, 数组被称为值类型数组。
如果存储的元素都是引用类型对象, 该数组被称为 引用类型数组。
定义和初始化一维数组
● 数组在必须在访问之前初始化,不能像这样访问数组或者给数组元素赋值:
int[] myIntArray;
myIntArray[10] = 5; //错误, 该数组没有初始化
● 数组初始化的第一种方式是:用字面值形式指定数组的完整内容。该数组的长度编译器可以通过数组中的元素个数来推断长度。
int [] array= {0,1,2,3,4,5};
● 第二种方式是:指定数组的大小,然后使用new来初始化数组,但是该数组的每一个元素都被自动初始化为该数组类型的默认值。 对于预定义类型, 整型默认为 0,浮点型默认值为0.0, 布尔型默认值为 false, 引用类型 默认值为 null。
int []intArr =new int[4];
● 第三种方式是:组合上面的初始化方式:
int [] array = new int[5] {0,1,2,3,4};
int [] array = new int[10] {0,1,2,3,4}; //错误
注意:使用这样的方式数组的大小与元素个数必须一致,否则错误。
● 第四种方式是:组合上面的初始化方式: 这样就不必输入数组的长度, 编译器可以通过元素个数来判断。
int[] array = new int[] { 0, 1, 2, 3, 4 };
● 第五种方式是:也可以使用非常量的变量来进行初始化(c++ 中不可以这样):
int a = 6;
int[] array = new int[a]; //输出 6个0
int a = 6;
int[] array = new int[a];
array[0] = 12; //给第一个元素,初始化
for (int tt = 0; tt < a; ++tt)
{
array[tt] = tt * 1; // 循环给每一个元素初始化
}
● 第六种方式是:如果想使用非常量的变量来进行初始化,并且同时指定数组元素的初始值,那么该变量,必须是个常量:
const int arraySize = 5;
int[] myIntArray=new int[arraySize] {0,1,2,3,4};
const int arraySize = 4;
int[] myIntArray = new int[arraySize] { 0, 1, 2 }; //错误,数组大小和元素个数不一致
如果省略了关键字const,语法错误。注意: 这样写,数组的大小与元素个数必须一致,否则错误。
定义和初始化矩形数组
● 要显式初始化矩形数组,需要注意的有:
每一个初始值元素必须封闭在大括号内。
每一个维度也必须嵌套并封闭在大括号内。
除了初始值,每一个维度的初始化列表和组成部分也必须使用逗号分隔。
矩形数组初始化的第一种方式:
int[,] array = new int[,] { { 10,1},{ 2,10},{ 11,9} };
第二种方式:
double[,] myDouble = new double[3, 4];
第三种方式:这个通过字面值赋值,隐式定义了数组的维度。
double[,] myDouble = {{1,2,3,4},{2,3,4,5},{3,4,5,6}};
第四种方式:
int c = 3, a = 4;
int[,] myInt = new int[c, a]; //正确
第五种方式:
const int a = 3;
const int b = 2;
int[,] array = new int[a, b] { { 10, 1 }, { 2, 10 }, { 11, 9 } };
第六种方式:
double[,] myDouble = new double[3,4] { { 1, 2, 3, 4 }, { 2, 3, 4, 5 }, { 3, 4, 5, 6 } };
其实初始化的方式跟一维数组的类似,只是维数不同罢了。
隐式类型数组 (var关键字)
● 数组可以是隐式类型的,比如:
当初始化数组时,我们使用关键字 var, 可以让编译器根据初始化语句的类型来推断数组类型。
int[] arr = new int[] { 10, 20 }; //一维数组
var arr2 = new[] { 10, 20, 30 };
int[,]arr3=new int[,]{ { 1,2},{ 2,4} };
var arr4=new[,] { { 1, 2 }, { 2, 4 } };
注意: 在隐式类型 arr4 声明中, 我们仍然需要在初始化中提供 秩说明符。
看一个小代码:
var myDouble =new[,] { { 1, 2, 3, 4 }, { 2, 3, 4, 5 }, { 3, 4, 5, 6 } };
foreach (double height in myDouble)
{
WriteLine(height);
}
初始化和定义交错数组(锯齿数组)
● 还有交错数组,其中每行的元素个数可能不同,其中的每一个元素都是包含另外一个数组。但是注意:这些数组都必须有相同的基本类型。 例如:
int[][] jagArr = new int[3][];
● 交错数组的声明语法要求每一个维度都要有一对独立的方括号。 方括号的数量决定了交错数组的维度。
交错数组可能的维度可以是大于1的任意整数。
和矩形数组一样, 在数组类型的[ ] 号中不允许写数组的维度长度。
int[][] arr; //维度是2
● 交错数组第一种初始化方式:
int[][] jagArr = new int[3][];
不能这样声明:
int[][] myInt =new[3][4];
也不可以这样:
int[][] myInt = { { 1, 3, 4 }, { 2, 3, 4, 5 }, { 22, 3 } };
● 第二种初始化方式:
int[][] Arr = new int[3][]; //实例化顶层数组
//在数组下标为0的位置, 该位置有又是一个数组,里面有2个数字,元素个数不可以多,也不可以少
Arr[0] = new int[2] { 10, 20}; //实例化顶层数组
Arr[1] = new int[4] { 40, 2, 3, 4 }; //实例化子数组
Arr[2] = new int[5] { 1, 2, 3, 4, 5 }; //实例化子数组
● 第三种初始化方式:
int[][] Arr = new int[3][]; //实例化顶层数组
//在数组下标为0的位置, 该位置有又是一个数组,[]不指定子数组的维度,让编译器根据元素个数去判断
Arr[0] = new int[] { 10, 20,30}; //实例化子数组
Arr[1] = new int[] { 40, 50, 60, 70 }; //实例化子数组
Arr[2] = new int[] { 80, 90, 100, 110, 120 }; //实例化子数组
在第二种和第三种初始化方式要注意的是: 在代码中, 每一个子数组的引用都赋值给了顶层数组的元素。
● 第四种初始化方式:
int[][] myInt = new int[3][] { new int[] { 1, 2, 3 }, new int[] { 1 }, new int[] { 1, 2 } }; //正确
foreach (int[] divisor in myInt)
{
foreach (int temp in divisor)
{
WriteLine(temp);
}
}
该交错数组有三行,new int[] { 1, 2, 3 } 表示,数组下标为0的地方有3个元素, new int[] { 1 } 表示数组下标为1的地方有1个元素,new int[] { 1, 2 } }表示数组下标为2的地方有2个元素. 最后使用嵌套的 foreach 循环输出 交错数组全部元素。
第五种初始化方式:
int[][] myInt = { new int[] { 1, 2, 3 }, new int[] { 1 }, new int[] { 1, 2 } }; //正确
foreach (int[] divisor in myInt)
{
foreach (int temp in divisor)
{
WriteLine(temp);
}
}
交错数组中的矩形数组
● 由于交错数组中的子数组本身就是数组, 因此交错数组中也可能有矩形数组。 第六种初始化方式:
namespace HelloWorld_Console
{
class Program
{
static void Main(string[] args)
{
int[][,] Arr = new int[3][,]; // 实例化带有3个二维数组的交错数组
Arr[0] = new int[,] { { 10, 20 },
{ 100, 200 } };
Arr[1] = new int[,] { { 30, 40, 50 },
{ 300, 400, 500 } };
Arr[2] = new int[,] { { 60, 70, 80, 90 },
{ 600, 700, 800, 900 } };
for (int i = 0; i < Arr.GetLength(0); i++)
{
for (int j = 0; j < Arr[i].GetLength(0); j++)
{
for (int k = 0; k < Arr[i].GetLength(1); k++)
{
Console.WriteLine
("[{0}][{1},{2}] = {3}", i, j, k, Arr[i][j, k]);
}
Console.WriteLine("");
}
Console.WriteLine("");
}
ReadKey();
}
}
}
输出结果为:
[0][0,0] = 10
[0][0,1] = 20
[0][1,0] = 100
[0][1,1] = 200
[1][0,0] = 30
[1][0,1] = 40
[1][0,2] = 50
[1][1,0] = 300
[1][1,1] = 400
[1][1,2] = 500
[2][0,0] = 60
[2][0,1] = 70
[2][0,2] = 80
[2][0,3] = 90
[2][1,0] = 600
[2][1,1] = 700
[2][1,2] = 800
[2][1,3] = 900
代码中使用了数组的继承 System.Array 的GetLength (int n) 方法来获取数组中指定维度的长度。
比较矩形数组 和交错 数组
矩形数组和交错数组的结构区别非常大。下图演示了 3*3 的矩形数组以及一个由3 个长度为3 的一维数组构成的交错数组的结构。
首先两个数组都保存了9个整数, 但是它们的结构去很不同。
矩形数组只有单个数组对象, 而交错数组有4个数组对象 。
注意: 一维数组有特定的指令用于性能优化, 矩形数组没有这些指令, 并且不在相同级别进行优化。 因此, 有时使用一维数组(可以被优化)的交错数组比矩形数组(不能被优化) 更有效率。