Skip to content

CSharp 交错数组的遍历与方法:玩转你的「不规则货架」 ​

引言:从整齐货架到灵活储物 ​

上一篇我们学会了怎么建交错数组——一排独立货架,每个货架层数不同。但现实开发中,你很少只建不用,更多时候需要:

  • 批量查看:把每个班级的成绩都打印出来
  • 批量修改:给所有学生的成绩加 5 分
  • 批量统计:计算每个班级的平均分、找出全校最高分
  • 灵活操作:合并两个班级、把成绩表转成 Excel 格式

这就像你有一排不规则货架,现在要逐层检查、整理、统计。怎么高效地做这些事?C# 提供了 4 种遍历方式和一整套实用方法,本文带你一一掌握。


一、基础回顾:交错数组的核心概念 ​

1.1 什么是交错数组(30 秒回顾) ​

交错数组(Jagged Array):数组的数组。外层是一个数组,里面的每个元素又是一个独立的一维数组,每个内层数组的长度可以不同。

生活类比:

类比说明
快递柜大中小三种柜子组合,每种柜子格数不同
停车场地下 2 层,每层车位数不同
班级1 班 30 人,2 班 25 人,3 班 32 人

与二维数组的关系:

  • 二维数组 int[,]:一整块连续内存,每行长度必须相同(矩形)
  • 交错数组 int[][]:外层存引用,内层分散,每行长度可以不同(锯齿)

「锯齿数组」就是「交错数组」的俗称,因为每行长度不同,画出来像锯齿一样参差不齐。

1.2 四种声明与初始化方式 ​

csharp
using System;

class Program
{
    static void Main()
    {
        // ===== 方式 1:只声明外层,内层逐个 new(最灵活) =====
        int[][] arr1 = new int[3][];        // 外层 3 个元素,内层全是 null
        arr1[0] = new int[2];               // 第 0 行:2 个元素,默认 0
        arr1[1] = new int[4];               // 第 1 行:4 个元素,默认 0
        arr1[2] = new int[1];               // 第 2 行:1 个元素,默认 0

        // ===== 方式 2:声明时直接初始化(推荐) =====
        int[][] arr2 = {
            new int[] { 90, 85 },           // 第 0 行:2 个元素
            new int[] { 88, 92, 60, 71 },   // 第 1 行:4 个元素
            new int[] { 76 }                // 第 2 行:1 个元素
        };

        // ===== 方式 3:省略 new int[],编译器推断(C# 3.0+) =====
        int[][] arr3 = {
            new[] { 1, 2 },                 // 等价于 new int[] { 1, 2 }
            new[] { 3, 4, 5 }
        };

        // ===== 方式 4:先声明变量,后分步赋值 =====
        int[][] arr4;                       // 此时是 null
        arr4 = new int[2][];                // 创建外层
        arr4[0] = new int[] { 1, 2, 3 };    // 创建第 0 行
        arr4[1] = new int[] { 4, 5 };       // 创建第 1 行

        // 访问验证
        Console.WriteLine(arr2[1][2]);      // 输出:60
        Console.WriteLine(arr3[1][0]);      // 输出:3
    }
}

逐行标注:

行代码作用注意事项
1int[][] arr1 = new int[3][];声明外层数组,长度 3内层全是 null!
2arr1[0] = new int[2];给第 0 行分配空间,长度 2不 new 就访问会抛 NullReferenceException
3arr1[1] = new int[4];第 1 行长度 4每行长度可以不同
4new int[] { 90, 85 }创建内层数组并直接赋值大括号里直接写元素
5new[] { 1, 2 }省略类型,编译器推断必须能推断出类型
6arr4 = new int[2][];先声明后赋值声明和创建可以分开

二、四种遍历方式全解析 ​

遍历交错数组就是把每个元素挨个访问一遍。C# 提供 4 种主流方案,各有适用场景。

2.1 方式一:嵌套 for 循环(全能选手) ​

是什么:外层循环走「行」(内层数组),内层循环走「列」(内层数组的元素)。

怎么用:

csharp
using System;

class Program
{
    static void Main()
    {
        int[][] classes = {
            new int[] { 90, 85 },
            new int[] { 88, 92, 60, 71 },
            new int[] { 76 }
        };

        // 外层 for:i 是班级号(第几个内层数组)
        for (int i = 0; i < classes.Length; i++)
        {
            Console.Write($"班级 {i}:");

            // 内层 for:j 是学生号,上界用「当前行自己的长度」
            for (int j = 0; j < classes[i].Length; j++)
            {
                Console.Write($"{classes[i][j]} ");
            }
            Console.WriteLine();   // 每打印完一个班换行
        }
    }
}

输出:

text
班级 0:90 85
班级 1:88 92 60 71
班级 2:76

逐行拆解:

  1. classes.Length —— 外层数组长度 = 班级数(3)
  2. classes[i].Length —— 第 i 个内层数组的长度 = 当前班人数
  3. classes[i][j] —— 第一对括号定位到班级,第二对括号定位到学生

什么时候用:

  • ✅ 需要行号/列号(如「第 2 班第 3 个学生」)
  • ✅ 需要修改元素(如 classes[i][j] += 5)
  • ✅ 需要按特定顺序访问(如逆序、跳行)
  • ✅ 性能敏感场景

优缺点:

  • 优点:功能最全,能拿索引,能改元素,性能最好
  • 缺点:代码略繁琐,内层上界写错会越界

性能:直接索引访问,速度最快。

2.2 方式二:foreach 循环(最省心) ​

是什么:编译器自动处理索引,你只需关心「对每个元素做什么」。

怎么用:

csharp
int[][] classes = {
    new int[] { 90, 85 },
    new int[] { 88, 92, 60, 71 },
    new int[] { 76 }
};

// 外层 foreach:拿到的是「每一行」(int[] 类型)
foreach (int[] classScores in classes)
{
    // 内层 foreach:遍历当前行的每个元素
    foreach (int score in classScores)
    {
        Console.Write(score + " ");
    }
    Console.WriteLine();
}

逐行拆解:

  • foreach (int[] classScores in classes) —— 每次循环拿到一个内层数组(int[])
  • foreach (int score in classScores) —— 遍历当前内层数组的每个元素
  • 编译器自动处理所有边界,永远不会越界

什么时候用:

  • ✅ 只读遍历(不修改元素)
  • ✅ 不需要行号/列号
  • ✅ 追求代码简洁、安全

优缺点:

  • 优点:代码极简、安全(编译器保证不越界)
  • 缺点:拿不到索引、不能修改值类型元素

性能:与 for 循环相当。

2.3 方式三:for + foreach 混合(灵活实用) ​

是什么:外层用 for 拿行号,内层用 foreach 遍历当前行,兼顾索引和简洁。

怎么用:

csharp
int[][] classes = {
    new int[] { 90, 85 },
    new int[] { 88, 92, 60, 71 },
    new int[] { 76 }
};

// 外层 for:需要班号来显示
for (int i = 0; i < classes.Length; i++)
{
    Console.Write($"班级 {i}({classes[i].Length} 人):");

    // 内层 foreach:只读遍历当前班,简洁安全
    foreach (int score in classes[i])
    {
        Console.Write(score + " ");
    }
    Console.WriteLine();
}

什么时候用:

  • ✅ 需要行号,但内层不需要列号
  • ✅ 交错数组遍历(内层长度各不相同)

2.4 方式四:LINQ 查询(函数式优雅) ​

是什么:用 LINQ 的链式调用处理交错数组,适合复杂筛选、转换、聚合。

怎么用:

csharp
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[][] classes = {
            new int[] { 90, 85 },
            new int[] { 88, 92, 60, 71 },
            new int[] { 76, 55, 82 }
        };

        // 1. SelectMany:把所有班级摊平成一个序列
        var allScores = classes.SelectMany(c => c);
        Console.WriteLine($"全校总分:{allScores.Sum()}");
        Console.WriteLine($"全校平均分:{allScores.Average():F1}");
        Console.WriteLine($"全校最高分:{allScores.Max()}");

        // 2. Select:对每个班级做聚合
        var classAverages = classes.Select(c => c.Average());
        foreach (var avg in classAverages)
        {
            Console.WriteLine($"班级平均分:{avg:F1}");
        }

        // 3. Where + SelectMany:筛选出所有及格的成绩
        var passed = classes.SelectMany(c => c).Where(s => s >= 60);
        Console.WriteLine($"及格人数:{passed.Count()}");

        // 4. 找出每个班的最高分
        var classMaxes = classes.Select(c => c.Max());
        for (int i = 0; i < classes.Length; i++)
        {
            Console.WriteLine($"班级 {i} 最高分:{classMaxes.ElementAt(i)}");
        }
    }
}

逐行拆解:

  • SelectMany(c => c) —— 把「数组的数组」摊平成一个序列
  • Select(c => c.Average()) —— 对每个内层数组计算平均值
  • Where(s => s >= 60) —— 筛选符合条件的元素
  • ElementAt(i) —— 按索引取序列中的元素

什么时候用:

  • ✅ 复杂筛选、转换、聚合
  • ✅ 追求代码简洁、可读性强

优缺点:

  • 优点:代码极简、功能强大
  • 缺点:性能略低、不能直接修改原数组

2.5 方式五:递归遍历(多层嵌套复杂场景) ​

是什么:当交错数组的嵌套层数不确定(如 int[][][]、int[][][][])时,用递归自动处理任意深度。

怎么用:

csharp
using System;

class Program
{
    static void Main()
    {
        // 三层交错数组:2 个年级,每个年级 2 个班,每班人数不同
        int[][][] school = {
            new int[][] {                   // 年级 0
                new int[] { 90, 85 },       // 班级 0
                new int[] { 88, 92, 60 }    // 班级 1
            },
            new int[][] {                   // 年级 1
                new int[] { 76 },           // 班级 0
                new int[] { 80, 95, 70, 88 } // 班级 1
            }
        };

        // 递归遍历任意深度的交错数组
        TraverseJagged(school, 0);
    }

    // depth:当前嵌套深度,用于缩进显示
    static void TraverseJagged(object arr, int depth)
    {
        // 如果是 int 类型,说明到最内层了
        if (arr is int value)
        {
            Console.Write(new string(' ', depth * 2) + value + " ");
            return;
        }

        // 如果是数组类型,继续递归
        if (arr is Array array)
        {
            for (int i = 0; i < array.Length; i++)
            {
                TraverseJagged(array.GetValue(i), depth + 1);
            }
            Console.WriteLine();   // 每完成一层换行
        }
    }
}

输出:

text
    90 85
    88 92 60
    76
    80 95 70 88

什么时候用:

  • ✅ 嵌套层数不确定的复杂交错数组
  • ✅ 需要统一处理任意深度的数据结构

优缺点:

  • 优点:能处理任意深度,代码通用
  • 缺点:有递归开销,理解门槛较高

2.6 四种遍历方式对比总结 ​

对比项嵌套 forforeachfor + foreachLINQ递归
能否拿索引✅❌✅(外层)❌✅
能否改元素✅❌✅(外层 for)❌✅
越界风险有无有(外层)无有
代码简洁度一般极简简洁极简复杂
性能最快接近 for接近 for略慢最慢
学习成本低低低中高
适用场景全能只读遍历需要行号复杂处理任意深度

实际建议:

  • 简单只读遍历用 foreach
  • 需要索引或修改用嵌套 for
  • 交错数组常规遍历用 for + foreach
  • 复杂数据处理用 LINQ
  • 任意深度嵌套用递归

2.7 空值处理与越界防范 ​

csharp
int[][] data = new int[3][];
data[0] = new int[] { 1, 2 };
// data[1] 忘记 new 了,是 null
data[2] = new int[] { 3 };

// 错误:直接遍历会抛 NullReferenceException
// foreach (int[] row in data) { ... }

// 正确:先判空再遍历
foreach (int[] row in data)
{
    if (row != null)   // 防御性判断
    {
        foreach (int n in row)
        {
            Console.Write(n + " ");
        }
    }
    else
    {
        Console.Write("(空行)");
    }
    Console.WriteLine();
}

// 越界防范:内层循环上界永远用 classes[i].Length
for (int i = 0; i < data.Length; i++)
{
    if (data[i] != null)
    {
        for (int j = 0; j < data[i].Length; j++)   // 关键:用当前行长度
        {
            Console.Write(data[i][j] + " ");
        }
    }
}

三、核心操作方法拓展 ​

3.1 数组扩容:添加新班级 ​

需求:原有 3 个班级,新增 1 个班级。

csharp
using System;

class Program
{
    static void Main()
    {
        int[][] classes = {
            new int[] { 90, 85 },
            new int[] { 88, 92, 60, 71 },
            new int[] { 76 }
        };

        // 数组长度固定,不能直接添加,需要「新建大数组 + 复制旧数据」
        int newSize = classes.Length + 1;
        int[][] newClasses = new int[newSize][];

        // 复制旧数据
        for (int i = 0; i < classes.Length; i++)
        {
            newClasses[i] = classes[i];   // 引用赋值,指向同一个内层数组
        }

        // 添加新班级
        newClasses[newSize - 1] = new int[] { 95, 88, 92 };

        classes = newClasses;   // 让原变量指向新数组

        Console.WriteLine($"现在有 {classes.Length} 个班级");
        Console.WriteLine($"新班级成绩:{string.Join(", ", classes[3])}");
    }
}

逐行拆解:

  1. int[][] newClasses = new int[newSize][]; —— 新建更大的外层数组
  2. newClasses[i] = classes[i]; —— 引用赋值,新旧外层共享内层数组(省内存)
  3. classes = newClasses; —— 原变量指向新数组,旧外层被 GC 回收

3.2 元素增删改查 ​

csharp
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[][] classes = {
            new int[] { 90, 85 },
            new int[] { 88, 92, 60, 71 },
            new int[] { 76 }
        };

        // ===== 查:读取元素 =====
        int score = classes[1][2];          // 1 班第 2 个学生:60

        // ===== 改:修改元素 =====
        classes[1][2] = 65;                 // 60 → 65

        // ===== 增:在 1 班末尾添加一个学生(需重建内层数组) =====
        int[] oldClass = classes[1];
        int[] newClass = new int[oldClass.Length + 1];
        Array.Copy(oldClass, newClass, oldClass.Length);
        newClass[newClass.Length - 1] = 95; // 新学生成绩
        classes[1] = newClass;              // 替换内层数组

        // ===== 删:删除 1 班第 0 个学生(用 LINQ) =====
        classes[1] = classes[1].Where((s, idx) => idx != 0).ToArray();

        // 验证
        Console.WriteLine($"1 班现在 {classes[1].Length} 人:{string.Join(", ", classes[1])}");
    }
}

关键提醒:

  • 数组长度固定,增删本质是「新建数组 + 复制数据」
  • 修改元素直接通过索引 classes[i][j] = value
  • 频繁增删建议用 List<List<int>> 替代

3.3 数组合并:两个班级合并成一个 ​

csharp
int[][] classA = { new int[] { 90, 85 } };
int[][] classB = { new int[] { 88, 92 }, new int[] { 76 } };

// 方式 1:新建大数组,手动复制
int[][] merged = new int[classA.Length + classB.Length][];
for (int i = 0; i < classA.Length; i++)
{
    merged[i] = classA[i];
}
for (int i = 0; i < classB.Length; i++)
{
    merged[classA.Length + i] = classB[i];
}

// 方式 2:用 LINQ 的 Concat(更简洁)
int[][] merged2 = classA.Concat(classB).ToArray();

Console.WriteLine($"合并后 {merged2.Length} 个班级");

3.4 维度转换:交错数组 ↔ 二维数组 ​

交错数组转二维数组:

csharp
using System;

class Program
{
    static void Main()
    {
        int[][] jagged = {
            new int[] { 1, 2 },
            new int[] { 3, 4, 5 },
            new int[] { 6 }
        };

        // 找最长行的长度,作为二维数组的列数
        int maxCols = 0;
        foreach (int[] row in jagged)
        {
            if (row.Length > maxCols) maxCols = row.Length;
        }

        // 创建二维数组,短行补默认值 0
        int[,] multi = new int[jagged.Length, maxCols];
        for (int i = 0; i < jagged.Length; i++)
        {
            for (int j = 0; j < jagged[i].Length; j++)
            {
                multi[i, j] = jagged[i][j];
            }
            // 短行剩余位置自动是 0
        }

        // 打印验证
        for (int i = 0; i < multi.GetLength(0); i++)
        {
            for (int j = 0; j < multi.GetLength(1); j++)
            {
                Console.Write($"{multi[i, j],3}");
            }
            Console.WriteLine();
        }
        // 输出:
        //   1  2  0
        //   3  4  5
        //   6  0  0
    }
}

二维数组转交错数组:

csharp
int[,] multi = {
    { 1, 2, 0 },
    { 3, 4, 5 },
    { 6, 0, 0 }
};

int[][] jagged = new int[multi.GetLength(0)][];
for (int i = 0; i < multi.GetLength(0); i++)
{
    // 计算当前行实际长度(去掉末尾的 0)
    int actualLength = multi.GetLength(1);
    while (actualLength > 0 && multi[i, actualLength - 1] == 0)
    {
        actualLength--;
    }

    jagged[i] = new int[actualLength];
    for (int j = 0; j < actualLength; j++)
    {
        jagged[i][j] = multi[i, j];
    }
}

3.5 深度复制:真正的独立副本 ​

csharp
int[][] original = {
    new int[] { 1, 2 },
    new int[] { 3, 4 }
};

// 错误:Clone() 是浅拷贝,内层仍共享
int[][] shallow = (int[][])original.Clone();
shallow[0][0] = 999;
Console.WriteLine(original[0][0]);   // 999(被污染!)

// 正确:逐行深拷贝
int[][] deep = new int[original.Length][];
for (int i = 0; i < original.Length; i++)
{
    deep[i] = (int[])original[i].Clone();
}
deep[0][0] = 888;
Console.WriteLine(original[0][0]);   // 999(不受影响)

四、常见踩坑点与避坑指南 ​

4.1 坑一:混淆交错数组与二维数组的内存分配 ​

错误认知:以为 int[][] 和 int[,] 一样,内存是连续的。

真相:

类型内存结构访问速度缓存友好度
int[,]一整块连续内存更快高
int[][]外层连续,内层分散略慢低

影响:处理超大数据量时(如 10000×10000),int[,] 性能明显优于 int[][]。

4.2 坑二:空行交错数组的处理 ​

场景:某些班级还没有学生,内层是 null 或空数组。

csharp
int[][] classes = new int[3][];
classes[0] = new int[] { 90, 85 };
classes[1] = null;                    // 空引用:还没分配
classes[2] = new int[0];              // 空数组:已分配但长度 0

// 三种状态的区别
Console.WriteLine(classes[1] == null);        // True
Console.WriteLine(classes[2] == null);        // False
Console.WriteLine(classes[2].Length);         // 0

// 安全遍历:同时处理 null 和空数组
foreach (int[] row in classes)
{
    if (row == null)
    {
        Console.WriteLine("(未分配)");
    }
    else if (row.Length == 0)
    {
        Console.WriteLine("(空班级)");
    }
    else
    {
        Console.WriteLine(string.Join(", ", row));
    }
}

4.3 坑三:LINQ 修改元素不生效 ​

错误代码:

csharp
int[][] classes = { new int[] { 90, 85 }, new int[] { 88, 92 } };

// 错误:LINQ 生成的是新序列,不改原数组
var boosted = classes.SelectMany(c => c).Select(s => s + 5);
Console.WriteLine(classes[0][0]);   // 输出:90(没变!)

修正:要修改原数组必须用嵌套 for:

csharp
for (int i = 0; i < classes.Length; i++)
{
    for (int j = 0; j < classes[i].Length; j++)
    {
        classes[i][j] += 5;
    }
}
Console.WriteLine(classes[0][0]);   // 输出:95(改了!)

五、实战场景案例 ​

5.1 案例一:学生成绩统计系统 ​

需求:3 个班级成绩统计,计算每班平均分、最高分、及格率,找出全校最高分。

csharp
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[][] classes = {
            new int[] { 90, 85 },
            new int[] { 88, 92, 60, 71 },
            new int[] { 76, 55, 82 }
        };

        // 1. 各班统计(嵌套 for)
        for (int i = 0; i < classes.Length; i++)
        {
            int sum = 0, max = classes[i][0], pass = 0;
            for (int j = 0; j < classes[i].Length; j++)
            {
                sum += classes[i][j];
                if (classes[i][j] > max) max = classes[i][j];
                if (classes[i][j] >= 60) pass++;
            }
            Console.WriteLine($"班级 {i}:平均 {sum / (double)classes[i].Length:F1}," +
                            $"最高 {max},及格率 {pass * 100.0 / classes[i].Length:F0}%");
        }

        // 2. 全校统计(LINQ)
        var all = classes.SelectMany(c => c);
        Console.WriteLine($"全校:{all.Count()} 人,平均 {all.Average():F1},最高 {all.Max()}");
    }
}

输出:

text
班级 0:平均 87.5,最高 90,及格率 100%
班级 1:平均 77.8,最高 92,及格率 100%
班级 2:平均 71.0,最高 82,及格率 67%
全校:9 人,平均 77.6,最高 92

5.2 案例二:多部门员工信息管理 ​

需求:公司 3 个部门,每部门人数不同。统计每部门平均工资、最高工资,给所有人涨薪 10%。

csharp
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        // 3 个部门,每部门员工工资
        double[][] departments = {
            new double[] { 8000, 12000, 15000 },        // 技术部
            new double[] { 6000, 8000 },                // 市场部
            new double[] { 5000, 5500, 6000, 7000 }     // 运营部
        };

        string[] deptNames = { "技术部", "市场部", "运营部" };

        // 1. 各部门统计
        for (int i = 0; i < departments.Length; i++)
        {
            double sum = 0, max = departments[i][0];
            for (int j = 0; j < departments[i].Length; j++)
            {
                sum += departments[i][j];
                if (departments[i][j] > max) max = departments[i][j];
            }
            Console.WriteLine($"{deptNames[i]}:{departments[i].Length} 人," +
                            $"平均 {sum / departments[i].Length:F0},最高 {max:F0}");
        }

        // 2. 全公司涨薪 10%(必须用嵌套 for 修改)
        for (int i = 0; i < departments.Length; i++)
        {
            for (int j = 0; j < departments[i].Length; j++)
            {
                departments[i][j] *= 1.1;
            }
        }

        // 3. 验证涨薪后技术部平均工资
        Console.WriteLine($"涨薪后技术部平均:{departments[0].Average():F0}");
    }
}

5.3 案例三:电商订单数据分析 ​

需求:统计 4 个用户的订单金额,计算每单总额、找出消费最高的用户。

csharp
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        // 4 个用户,每人订单数不同
        double[][] orders = {
            new double[] { 99.9, 199.0, 59.5 },     // 用户 0:3 单
            new double[] { 299.0 },                   // 用户 1:1 单
            new double[] { 49.9, 89.0, 129.0, 39.9 }, // 用户 2:4 单
            new double[] { 199.0, 299.0 }             // 用户 3:2 单
        };

        // 1. 每用户消费总额
        for (int i = 0; i < orders.Length; i++)
        {
            double total = 0;
            foreach (double order in orders[i])
            {
                total += order;
            }
            Console.WriteLine($"用户 {i}:{orders[i].Length} 单,共 ¥{total:F2}");
        }

        // 2. 找出消费最高的用户(嵌套 for 记录位置)
        double maxTotal = 0;
        int maxUser = 0;
        for (int i = 0; i < orders.Length; i++)
        {
            double total = orders[i].Sum();
            if (total > maxTotal)
            {
                maxTotal = total;
                maxUser = i;
            }
        }
        Console.WriteLine($"消费最高:用户 {maxUser},共 ¥{maxTotal:F2}");
    }
}

六、知识点总结 ​

  1. 交错数组 = 数组的数组:外层存引用,内层分散,每行长度可以不同
  2. 四种遍历方式:嵌套 for(全能)、foreach(省心)、for+foreach(灵活)、LINQ(优雅)
  3. 递归遍历:处理任意深度嵌套的复杂交错数组
  4. 核心操作:扩容需新建大数组、增删本质是复制、合并用 Concat、维度转换手动处理
  5. 避坑要点:判空(null vs 空数组)、LINQ 不改原数组、[,] 比 [][] 缓存更友好
  6. 选型原则:规则数据用 [,],不规则数据用 [][],频繁增删用 List<List<T>>

七、基础自测习题(10 道) ​

  1. 声明练习:声明一个交错数组,外层长度 3,内层长度分别为 2、3、4,填入数字 1~9。

  2. 遍历练习:用嵌套 for 遍历以下数组,输出格式为「行号:元素1 元素2 ...」:

csharp
int[][] data = { new[] { 5, 10 }, new[] { 15, 20, 25 }, new[] { 30 } };
  1. 修改练习:把上面数组中所有大于 15 的元素都乘以 2,输出修改后的数组。

  2. 统计练习:计算上面数组的总和、平均值、最大值、最小值。

  3. 查找练习:找出上面数组中最大值的位置(第几行第几列)。

  4. 扩容练习:给上面数组添加一个新行 { 40, 50 },输出扩容后的数组。

  5. 合并练习:把 { new[] { 1, 2 } } 和 { new[] { 3 }, new[] { 4, 5 } } 合并成一个交错数组。

  6. 转换练习:把交错数组 { new[] { 1, 2 }, new[] { 3, 4, 5 } } 转成 2×3 的二维数组,短行补 0。

  7. 判空练习:遍历以下数组,对 null 行输出「空」,对非空行输出元素:

csharp
int[][] data = new int[3][];
data[0] = new int[] { 1 };
data[2] = new int[] { 2, 3 };
  1. 综合练习:有一个交错数组表示 3 个小组的分数:{ new[]{80,90}, new[]{70,85,95}, new[]{60,75}, new[]{88} },找出:
    • 每个小组的平均分
    • 全校最高分的位置(第几组第几人)
    • 及格率最高的小组编号

答案解析 ​

第 1 题:

csharp
int[][] arr = new int[3][];
arr[0] = new int[] { 1, 2 };
arr[1] = new int[] { 3, 4, 5 };
arr[2] = new int[] { 6, 7, 8, 9 };

第 2 题:

csharp
for (int i = 0; i < data.Length; i++)
{
    Console.Write($"{i}:");
    for (int j = 0; j < data[i].Length; j++)
    {
        Console.Write(data[i][j] + " ");
    }
    Console.WriteLine();
}

第 3 题:

csharp
for (int i = 0; i < data.Length; i++)
{
    for (int j = 0; j < data[i].Length; j++)
    {
        if (data[i][j] > 15) data[i][j] *= 2;
    }
}

第 4 题:

csharp
int sum = 0, max = int.MinValue, min = int.MaxValue, count = 0;
foreach (int[] row in data)
{
    foreach (int n in row)
    {
        sum += n; count++;
        if (n > max) max = n;
        if (n < min) min = n;
    }
}
Console.WriteLine($"总和:{sum},平均:{sum / (double)count:F1},最大:{max},最小:{min}");

第 5 题:

csharp
int max = int.MinValue, maxI = 0, maxJ = 0;
for (int i = 0; i < data.Length; i++)
{
    for (int j = 0; j < data[i].Length; j++)
    {
        if (data[i][j] > max)
        {
            max = data[i][j]; maxI = i; maxJ = j;
        }
    }
}
Console.WriteLine($"最大值 {max} 在 [{maxI}][{maxJ}]");

第 6 题:

csharp
int[][] newArr = new int[data.Length + 1][];
for (int i = 0; i < data.Length; i++) newArr[i] = data[i];
newArr[data.Length] = new int[] { 40, 50 };
data = newArr;

第 7 题:

csharp
int[][] merged = a.Concat(b).ToArray();

第 8 题:

csharp
int maxCols = jagged.Max(r => r.Length);
int[,] multi = new int[jagged.Length, maxCols];
for (int i = 0; i < jagged.Length; i++)
{
    for (int j = 0; j < jagged[i].Length; j++)
    {
        multi[i, j] = jagged[i][j];
    }
}

第 9 题:

csharp
foreach (int[] row in data)
{
    if (row == null) Console.WriteLine("空");
    else Console.WriteLine(string.Join(", ", row));
}

第 10 题:

csharp
for (int i = 0; i < groups.Length; i++)
{
    double avg = groups[i].Average();
    int pass = groups[i].Count(s => s >= 60);
    Console.WriteLine($"组 {i}:平均 {avg:F1},及格率 {pass * 100.0 / groups[i].Length:F0}%");
}
// 最高分位置、及格率最高组号:用嵌套 for 记录即可

掌握了交错数组的遍历和方法,你已经能灵活处理各种不规则数据了。下一篇我们将进入 C# 方法的世界,学习如何封装可复用的代码块,让程序结构更清晰。