Skip to content

11-数组

数组是一种数据结构,用于存储相同类型的多个元素。数组在内存中是连续存储的,通过索引访问元素,具有极高的访问效率。


一、数组的声明和初始化

声明数组

csharp
// 方式一:声明后初始化
int[] numbers;                         // 声明(未分配内存)
numbers = new int[5];                  // 初始化:分配 5 个元素的空间,默认值为 0

// 方式二:声明并初始化
int[] arr1 = new int[3];              // 默认值:{0, 0, 0}
int[] arr2 = new int[] { 1, 2, 3 };   // 初始化器
int[] arr3 = { 1, 2, 3 };             // 简化语法(编译器推断类型)
var arr4 = new int[] { 1, 2, 3 };     // 使用 var

数组默认值

元素类型默认值
数值类型(int, double 等)0
boolfalse
char'\0'
引用类型(string 等)null
csharp
int[] intArr = new int[3];      // {0, 0, 0}
bool[] boolArr = new bool[3];   // {false, false, false}
string[] strArr = new string[3]; // {null, null, null}

二、数组的访问和遍历

通过索引访问

索引从 0 开始,最大索引为 Length - 1

csharp
int[] arr = { 10, 20, 30, 40, 50 };

// 读取
Console.WriteLine(arr[0]);  // 输出:10
Console.WriteLine(arr[4]);  // 输出:50

// 修改
arr[1] = 25;
Console.WriteLine(arr[1]);  // 输出:25

// 越界访问
// Console.WriteLine(arr[5]);  // IndexOutOfRangeException:索引超出范围

遍历数组(三种方式对比)

csharp
int[] arr = { 10, 20, 30, 40, 50 };

// 方式一:for 循环——需要索引时使用
for (int i = 0; i < arr.Length; i++)
{
    Console.WriteLine($"arr[{i}] = {arr[i]}");
}

// 方式二:foreach 循环——不需要索引,更简洁
foreach (int item in arr)
{
    Console.WriteLine(item);
}

// 方式三:while 循环——不常用
int index = 0;
while (index < arr.Length)
{
    Console.WriteLine(arr[index]);
    index++;
}

for 与 foreach 对比

对比项forforeach
是否需要索引
能否修改元素能(通过索引)不能(迭代变量只读)
能否反向遍历不能
可读性一般
适用场景需要索引、修改元素只读遍历所有元素

三、多维数组

矩形二维数组

每一行的列数相同,在内存中连续存储。

csharp
// 声明方式
int[,] matrix = new int[3, 4];  // 3行4列

// 初始化
int[,] matrix = {
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 },
    { 9, 10, 11, 12 }
};

// 访问
Console.WriteLine(matrix[0, 0]);  // 第一行第一列:1
Console.WriteLine(matrix[1, 2]);  // 第二行第三列:7

// 遍历
int rows = matrix.GetLength(0);  // 行数:3
int cols = matrix.GetLength(1);  // 列数:4

for (int i = 0; i < rows; i++)
{
    for (int j = 0; j < cols; j++)
    {
        Console.Write($"{matrix[i, j]}\t");
    }
    Console.WriteLine();
}

三维数组

csharp
// 2 层 × 3 行 × 4 列
int[,,] cube = new int[2, 3, 4];
Console.WriteLine(cube.Rank);       // 维度:3
Console.WriteLine(cube.GetLength(0)); // 第一维:2
Console.WriteLine(cube.GetLength(1)); // 第二维:3
Console.WriteLine(cube.GetLength(2)); // 第三维:4

矩形数组 vs 交错数组

对比项矩形数组 [,]交错数组 [][]
子数组长度所有行长度相同每行长度可以不同
内存布局连续一块每行独立分配
初始化new int[3,4]new int[3][],每行再 new
访问语法arr[i,j]arr[i][j]
性能较好(连续内存)稍差(多次间接访问)

四、交错数组(锯齿数组)

数组的每个元素又是一个数组,且子数组长度可以不同。

csharp
// 声明
int[][] jagged = new int[3][];

// 初始化每个子数组(长度可以不同)
jagged[0] = new int[] { 1, 2 };           // 长度 2
jagged[1] = new int[] { 3, 4, 5 };        // 长度 3
jagged[2] = new int[] { 6, 7, 8, 9 };     // 长度 4

// 访问
Console.WriteLine(jagged[0][1]);  // 第一个数组第二个元素:2
Console.WriteLine(jagged[2][3]);  // 第三个数组第四个元素:9

// 遍历
for (int i = 0; i < jagged.Length; i++)
{
    Console.Write($"第{i}行:");
    for (int j = 0; j < jagged[i].Length; j++)
    {
        Console.Write($"{jagged[i][j]} ");
    }
    Console.WriteLine();
}

五、Array 类的静态方法

System.Array 类提供了丰富的静态方法操作数组。

排序和反转

csharp
int[] arr = { 5, 2, 8, 1, 9 };

Array.Sort(arr);     // 升序排序:{1, 2, 5, 8, 9}
Array.Reverse(arr);  // 反转:{9, 8, 5, 2, 1}(Sort 后再 Reverse 可得到降序)

查找

csharp
int[] arr = { 3, 7, 1, 9, 4, 7, 2 };

// IndexOf:查找第一个匹配项的索引
int idx1 = Array.IndexOf(arr, 7);      // 1(第一个 7 的位置)
int idx2 = Array.IndexOf(arr, 99);     // -1(未找到)

// LastIndexOf:从后查找
int idx3 = Array.LastIndexOf(arr, 7);  // 5(最后一个 7 的位置)

// Exists:检查是否存在
bool hasEven = Array.Exists(arr, x => x % 2 == 0);  // true

// Find:查找第一个匹配
int first = Array.Find(arr, x => x > 5);   // 7

// FindAll:查找所有匹配
int[] all = Array.FindAll(arr, x => x > 5);  // {7, 9, 7}

// FindIndex:查找第一个匹配的索引
int idx = Array.FindIndex(arr, x => x > 5);   // 1

// TrueForAll:是否全部满足
bool allPositive = Array.TrueForAll(arr, x => x > 0);  // true

复制和调整

csharp
int[] source = { 1, 2, 3, 4, 5 };
int[] dest = new int[5];

// Copy:复制元素
Array.Copy(source, dest, source.Length);  // {1, 2, 3, 4, 5}

// Clone:返回副本
int[] cloned = (int[])source.Clone();
Console.WriteLine(cloned == source);  // False(不同引用)

// Clear:清空指定范围
Array.Clear(source, 1, 3);  // {1, 0, 0, 0, 5}

// Resize:调整大小(可能创建新数组)
Array.Resize(ref source, 8);  // {1, 0, 0, 0, 5, 0, 0, 0}
Array.Resize(ref source, 3);  // {1, 0, 0}(截断)

六、数组的局限性

局限说明替代方案
长度固定创建后不能增删元素List<T>
类型固定只能存储声明时的类型ArrayListobject[]
插入/删除不便中间操作需要移动大量元素LinkedList<T>
查找不便需要遍历或使用 Array 静态方法Dictionary<K,V>
csharp
// 数组长度不可变的示例
int[] arr = { 1, 2, 3 };
// 需要添加元素时,只能创建新数组
int[] newArr = new int[4];
Array.Copy(arr, newArr, arr.Length);
newArr[3] = 4;
arr = newArr;  // 重新赋值

七、数组实用技巧

数组转字符串

csharp
int[] arr = { 1, 2, 3 };

string str1 = string.Join(", ", arr);
Console.WriteLine(str1);  // 1, 2, 3

string str2 = string.Join(" | ", arr.Select(x => $"[{x}]"));
Console.WriteLine(str2);  // [1] | [2] | [3]

数组转 List

csharp
int[] arr = { 1, 2, 3 };
List<int> list = arr.ToList();
list.Add(4);  // List 可以动态增长

初始化特定值的数组

csharp
// 全部初始化为同一个值
int[] zeros = new int[10];           // 全部为 0(默认)
int[] fives = Enumerable.Repeat(5, 10).ToArray();  // 全部为 5

// 生成连续序列
int[] sequence = Enumerable.Range(1, 10).ToArray();  // {1, 2, 3, ..., 10}

// 生成随机数组
Random rnd = new Random();
int[] randomArr = Enumerable.Range(0, 10).Select(_ => rnd.Next(1, 100)).ToArray();

八、综合案例

案例:学生成绩分析

csharp
static void Main()
{
    int[] scores = { 85, 92, 78, 63, 95, 88, 72, 59, 100, 81 };

    // 排序
    Array.Sort(scores);

    // 基本信息
    Console.WriteLine($"学生人数:{scores.Length}");
    Console.WriteLine($"最低分:{scores[0]}");
    Console.WriteLine($"最高分:{scores[scores.Length - 1]}");

    // 平均分
    double avg = scores.Average();
    Console.WriteLine($"平均分:{avg:F1}");

    // 找出及格和不及格
    int[] passed = Array.FindAll(scores, s => s >= 60);
    int[] failed = Array.FindAll(scores, s => s < 60);

    Console.WriteLine($"及格人数:{passed.Length}({passed.Length * 100.0 / scores.Length:F1}%)");
    Console.WriteLine($"不及格人数:{failed.Length}({failed.Length * 100.0 / scores.Length:F1}%)");

    // 统计各分数段
    int[] levels = { 0, 60, 70, 80, 90, 101 };
    string[] levelNames = { "不及格", "及格", "中等", "良好", "优秀" };

    for (int i = 0; i < levelNames.Length; i++)
    {
        int count = Array.FindAll(scores, s => s >= levels[i] && s < levels[i + 1]).Length;
        Console.WriteLine($"{levelNames[i]}:{count} 人");
    }
}

核心知识点总结

数组创建方式速查

csharp
int[] a1 = new int[3];              // 指定长度,默认值
int[] a2 = new int[] { 1, 2, 3 };   // 初始化器
int[] a3 = { 1, 2, 3 };             // 简化语法
int[,] a4 = new int[2, 3];          // 二维矩形数组
int[][] a5 = new int[3][];          // 交错数组

注意事项

  1. 索引从 0 开始,有效范围 0 ~ Length-1
  2. 越界异常:访问超出范围的索引会抛出 IndexOutOfRangeException
  3. 数组是引用类型:赋值给另一个变量时传递的是引用(不是副本)
    csharp
    int[] a = { 1, 2, 3 };
    int[] b = a;      // b 和 a 指向同一个数组
    b[0] = 100;
    Console.WriteLine(a[0]);  // 输出:100(被 b 修改了)
    
    // 如需独立副本:
    int[] c = (int[])a.Clone();
  4. 数组长度不可变:需要动态长度时使用 List<T>
  5. Array.Sort 会修改原数组:如需保留原数组,先复制再排序

Released under the MIT License.