数组是一个存储相同类型元素的固定大小的顺序集合。
在元素类型后面加上方括号表示数组:
char[] vowels = new char[5]; // Declare an array of 5 characters
访问特定元素可以使用方括号加索引来实现:
vowels[0] = 'a';
vowels[1] = 'e';
vowels[2] = 'i';
vowels[3] = 'o';
vowels[4] = 'u';
Console.WriteLine (vowels[1]); // e
可以使用for循环语句迭代遍历数组中的每个元素:
for (int i = 0; i < vowels.Length; i++)
Console.Write (vowels[i]); // aeiou
数组的Length
属性返回数组中的元素个数。数组一旦被创建,它的大小就不能被改变。System.Collection
命名空间和子命名空间提供更高级别的数据结构,比如动态数组与字典。
数组初始化表达式允许在单个步骤中声明和填充数组:
char[] vowels = new char[] {'a','e','i','o','u'};
或者,
char[] vowels = {'a','e','i','o','u'};
所有数组继承自System.Array
类,为所有数组提供共同的服务。这些成员包括获取 (get) 或设置 (set) 元素的方法,无论数组类型是什么。
默认元素初始化
创建数组总是使用默认值预先初始化元素。某种类型的默认值是其内存按位 (bitwise) 归零的结果。
int[] a = new int[1000];
Console.Write (a[123]); // 0
值类型 VS 引用类型
数组元素类型是值类型还是引用类型对性能有重要影响。当元素类型为值类型时,每个元素值都作为数组的一部分进行分配。例如:
public struct Point { public int X, Y; }
...
Point[] a = new Point[1000];
int x = a[500].X; // 0
如果Point是一个类,那么创建数组只会分配1000个空引用:
public class Point { public int X, Y; }
...
Point[] a = new Point[1000];
int x = a[500].X; // Runtime error, NullReferenceException
为避免此错误,必须在实例化数组后显式实例化1000个Point:
Point[] a = new Point[1000];
for (int i = 0; i < a.Length; i++) // Iterate i from 0 to 999
a[i] = new Point(); // Set array element i with new point
数组本身始终是引用类型对象,而与元素类型无关。例如,下面语句是合法的:
int[] a = null;
多维数组 (Multidimensional Arrays)
多维数组有两种:矩形数组 (rectangular) 和锯齿数组 (jagged)。矩形数组表示一个n维内存块,锯齿数组是数组的数组。
矩形数组 (Rectangular arrays)
矩形数组使用逗号来分隔每个维度。下面声明了一个矩形二维数组,其中维度为3乘3:
int[,] matrix = new int[3,3];
数组的GetLength
方法返回给定维度的长度 (从0开始):
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
matrix[i,j] = i * 3 + j;
可以按如下方式初始化矩形数组 (创建与上面代码示例相同的数组):
int[,] matrix = new int[,]
{
{0,1,2},
{3,4,5},
{6,7,8}
};
锯齿数组 (Jagged arrays)
锯齿数组使用连续的方括号来表示每个维度。下面是一个声明锯齿二维数组的示例,其中最外层的维度是3:
int[][] matrix = new int[3][];
为什么是 new int[3][]
,而不是 new int[][3]
,可以查看 Eric Lippert 的文章 http://albahari.com/jagged。
声明中没有指定内部维度,因为与矩形数组不同,每个内部数组可以是任意长度。每个内部数组都隐式初始化为 null,而不是空数组。必须手动创建每个内部数组:
for (int i = 0; i < matrix.Length; i++)
{
matrix[i] = new int[3]; // Create inner array
for (int j = 0; j < matrix[i].Length; j++)
matrix[i][j] = i * 3 + j;
}
锯齿数组可以按如下方式初始化:
int[][] matrix = new int[][]
{
new int[] {0,1,2},
new int[] {3,4,5},
new int[] {6,7,8,9}
};
简化的数组初始化表达式
缩短数组初始化表达式有两种方法。第一种方法是省略new
操作符和类型限定符:
char[] vowels = {'a','e','i','o','u'};
int[,] rectangularMatrix =
{
{0,1,2},
{3,4,5},
{6,7,8}
};
int[][] jaggedMatrix =
{
new int[] {0,1,2},
new int[] {3,4,5},
new int[] {6,7,8}
};
第二种方法是使用var
关键字,它告诉编译器隐式地确定一个局部变量的类型:
var i = 3; // i is implicitly of type int
var s = "sausage"; // s is implicitly of type string
// Therefore:
var rectMatrix = new int[,] // rectMatrix is implicitly of type int[,]
{
{0,1,2},
{3,4,5},
{6,7,8}
};
var jaggedMat = new int[][] // jaggedMat is implicitly of type int[][]
{
new int[] {0,1,2},
new int[] {3,4,5},
new int[] {6,7,8}
};
隐式类型可以在数组中进一步使用:可以在new
关键字之后省略类型限定符,并让编译器推断数组类型:
var vowels = new[] {'a','e','i','o','u'}; // Compiler infers char[]
为了实现这一点,所有元素都能够隐式地转换为某单个类型 (并且至少有一个元素必须是该类型,并且必须只有一个最佳类型)。例如:
var x = new[] {1,10000000000}; // all convertible to long
边界检查
运行时将检查所有数组索引边界。如果使用无效索引,将引发IndexOutOfRangeException
:
int[] arr = new int[3];
arr[3] = 1; // IndexOutOfRangeException thrown
数组边界检查对于类型安全和简化调试是必要的。
练习
题目:给定一组数据 nums,返回小于数值 a 的数据。
public class Solution {
public int[] NumberOfArithmeticSlices(int[] nums, int a) {
int cnt = 0;
int n = nums.Length;
bool[] bLess = new bool[n];
for(int i=0; i<n; ++i){
if(nums[i]<a){
bLess[i] = true;
++cnt;
}
else{
bLess[i] = false;
}
}
int[] ans = new int[cnt];
int pos = 0;
for(int i=0; i<n; ++i){
if(bLess[i]==true){
ans[pos] = nums[i];
++pos;
}
}
return ans;
}
}