CSharp 交错数组的遍历与方法:玩转你的「不规则货架」
引言:从整齐货架到灵活储物
上一篇我们学会了怎么建交错数组——一排独立货架,每个货架层数不同。但现实开发中,你很少只建不用,更多时候需要:
- 批量查看:把每个班级的成绩都打印出来
- 批量修改:给所有学生的成绩加 5 分
- 批量统计:计算每个班级的平均分、找出全校最高分
- 灵活操作:合并两个班级、把成绩表转成 Excel 格式
这就像你有一排不规则货架,现在要逐层检查、整理、统计。怎么高效地做这些事?C# 提供了 4 种遍历方式和一整套实用方法,本文带你一一掌握。
一、基础回顾:交错数组的核心概念
1.1 什么是交错数组(30 秒回顾)
交错数组(Jagged Array):数组的数组。外层是一个数组,里面的每个元素又是一个独立的一维数组,每个内层数组的长度可以不同。
生活类比:
| 类比 | 说明 |
|---|---|
| 快递柜 | 大中小三种柜子组合,每种柜子格数不同 |
| 停车场 | 地下 2 层,每层车位数不同 |
| 班级 | 1 班 30 人,2 班 25 人,3 班 32 人 |
与二维数组的关系:
- 二维数组
int[,]:一整块连续内存,每行长度必须相同(矩形) - 交错数组
int[][]:外层存引用,内层分散,每行长度可以不同(锯齿)
「锯齿数组」就是「交错数组」的俗称,因为每行长度不同,画出来像锯齿一样参差不齐。
1.2 四种声明与初始化方式
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
}
}逐行标注:
| 行 | 代码 | 作用 | 注意事项 |
|---|---|---|---|
| 1 | int[][] arr1 = new int[3][]; | 声明外层数组,长度 3 | 内层全是 null! |
| 2 | arr1[0] = new int[2]; | 给第 0 行分配空间,长度 2 | 不 new 就访问会抛 NullReferenceException |
| 3 | arr1[1] = new int[4]; | 第 1 行长度 4 | 每行长度可以不同 |
| 4 | new int[] { 90, 85 } | 创建内层数组并直接赋值 | 大括号里直接写元素 |
| 5 | new[] { 1, 2 } | 省略类型,编译器推断 | 必须能推断出类型 |
| 6 | arr4 = new int[2][]; | 先声明后赋值 | 声明和创建可以分开 |
二、四种遍历方式全解析
遍历交错数组就是把每个元素挨个访问一遍。C# 提供 4 种主流方案,各有适用场景。
2.1 方式一:嵌套 for 循环(全能选手)
是什么:外层循环走「行」(内层数组),内层循环走「列」(内层数组的元素)。
怎么用:
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(); // 每打印完一个班换行
}
}
}输出:
班级 0:90 85
班级 1:88 92 60 71
班级 2:76逐行拆解:
classes.Length—— 外层数组长度 = 班级数(3)classes[i].Length—— 第 i 个内层数组的长度 = 当前班人数classes[i][j]—— 第一对括号定位到班级,第二对括号定位到学生
什么时候用:
- ✅ 需要行号/列号(如「第 2 班第 3 个学生」)
- ✅ 需要修改元素(如
classes[i][j] += 5) - ✅ 需要按特定顺序访问(如逆序、跳行)
- ✅ 性能敏感场景
优缺点:
- 优点:功能最全,能拿索引,能改元素,性能最好
- 缺点:代码略繁琐,内层上界写错会越界
性能:直接索引访问,速度最快。
2.2 方式二:foreach 循环(最省心)
是什么:编译器自动处理索引,你只需关心「对每个元素做什么」。
怎么用:
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 遍历当前行,兼顾索引和简洁。
怎么用:
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 的链式调用处理交错数组,适合复杂筛选、转换、聚合。
怎么用:
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[][][][])时,用递归自动处理任意深度。
怎么用:
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(); // 每完成一层换行
}
}
}输出:
90 85
88 92 60
76
80 95 70 88什么时候用:
- ✅ 嵌套层数不确定的复杂交错数组
- ✅ 需要统一处理任意深度的数据结构
优缺点:
- 优点:能处理任意深度,代码通用
- 缺点:有递归开销,理解门槛较高
2.6 四种遍历方式对比总结
| 对比项 | 嵌套 for | foreach | for + foreach | LINQ | 递归 |
|---|---|---|---|---|---|
| 能否拿索引 | ✅ | ❌ | ✅(外层) | ❌ | ✅ |
| 能否改元素 | ✅ | ❌ | ✅(外层 for) | ❌ | ✅ |
| 越界风险 | 有 | 无 | 有(外层) | 无 | 有 |
| 代码简洁度 | 一般 | 极简 | 简洁 | 极简 | 复杂 |
| 性能 | 最快 | 接近 for | 接近 for | 略慢 | 最慢 |
| 学习成本 | 低 | 低 | 低 | 中 | 高 |
| 适用场景 | 全能 | 只读遍历 | 需要行号 | 复杂处理 | 任意深度 |
实际建议:
- 简单只读遍历用
foreach - 需要索引或修改用嵌套
for - 交错数组常规遍历用
for + foreach - 复杂数据处理用 LINQ
- 任意深度嵌套用递归
2.7 空值处理与越界防范
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 个班级。
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])}");
}
}逐行拆解:
int[][] newClasses = new int[newSize][];—— 新建更大的外层数组newClasses[i] = classes[i];—— 引用赋值,新旧外层共享内层数组(省内存)classes = newClasses;—— 原变量指向新数组,旧外层被 GC 回收
3.2 元素增删改查
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 数组合并:两个班级合并成一个
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 维度转换:交错数组 ↔ 二维数组
交错数组转二维数组:
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
}
}二维数组转交错数组:
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 深度复制:真正的独立副本
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 或空数组。
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 修改元素不生效
错误代码:
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:
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 个班级成绩统计,计算每班平均分、最高分、及格率,找出全校最高分。
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()}");
}
}输出:
班级 0:平均 87.5,最高 90,及格率 100%
班级 1:平均 77.8,最高 92,及格率 100%
班级 2:平均 71.0,最高 82,及格率 67%
全校:9 人,平均 77.6,最高 925.2 案例二:多部门员工信息管理
需求:公司 3 个部门,每部门人数不同。统计每部门平均工资、最高工资,给所有人涨薪 10%。
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 个用户的订单金额,计算每单总额、找出消费最高的用户。
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}");
}
}六、知识点总结
- 交错数组 = 数组的数组:外层存引用,内层分散,每行长度可以不同
- 四种遍历方式:嵌套 for(全能)、foreach(省心)、for+foreach(灵活)、LINQ(优雅)
- 递归遍历:处理任意深度嵌套的复杂交错数组
- 核心操作:扩容需新建大数组、增删本质是复制、合并用 Concat、维度转换手动处理
- 避坑要点:判空(null vs 空数组)、LINQ 不改原数组、
[,]比[][]缓存更友好 - 选型原则:规则数据用
[,],不规则数据用[][],频繁增删用List<List<T>>
七、基础自测习题(10 道)
声明练习:声明一个交错数组,外层长度 3,内层长度分别为 2、3、4,填入数字 1~9。
遍历练习:用嵌套 for 遍历以下数组,输出格式为「行号:元素1 元素2 ...」:
int[][] data = { new[] { 5, 10 }, new[] { 15, 20, 25 }, new[] { 30 } };修改练习:把上面数组中所有大于 15 的元素都乘以 2,输出修改后的数组。
统计练习:计算上面数组的总和、平均值、最大值、最小值。
查找练习:找出上面数组中最大值的位置(第几行第几列)。
扩容练习:给上面数组添加一个新行
{ 40, 50 },输出扩容后的数组。合并练习:把
{ new[] { 1, 2 } }和{ new[] { 3 }, new[] { 4, 5 } }合并成一个交错数组。转换练习:把交错数组
{ new[] { 1, 2 }, new[] { 3, 4, 5 } }转成 2×3 的二维数组,短行补 0。判空练习:遍历以下数组,对 null 行输出「空」,对非空行输出元素:
int[][] data = new int[3][];
data[0] = new int[] { 1 };
data[2] = new int[] { 2, 3 };- 综合练习:有一个交错数组表示 3 个小组的分数:
{ new[]{80,90}, new[]{70,85,95}, new[]{60,75}, new[]{88} },找出:- 每个小组的平均分
- 全校最高分的位置(第几组第几人)
- 及格率最高的小组编号
答案解析
第 1 题:
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 题:
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 题:
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 题:
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 题:
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 题:
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 题:
int[][] merged = a.Concat(b).ToArray();第 8 题:
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 题:
foreach (int[] row in data)
{
if (row == null) Console.WriteLine("空");
else Console.WriteLine(string.Join(", ", row));
}第 10 题:
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# 方法的世界,学习如何封装可复用的代码块,让程序结构更清晰。