一、数组方法进阶(Array
类核心方法解析)
该部分主要介绍Array
类的静态方法与实例方法,核心是高阶函数(参数为函数的方法)的应用,通过回调函数实现灵活的数组查询与操作。
1. 核心静态方法(Array.XXX
)
静态方法需通过Array
类直接调用,主要用于数组查询、判断、遍历等,参数常包含 "回调函数"(定义查询条件)。
方法 | 作用 | 关键参数 | 示例场景 |
---|---|---|---|
Find |
从前向后查找第一个满足条件的元素 | 数组 + 回调函数(返回bool ,表示元素是否符合条件) |
查找第一个不满 18 岁的年龄(Array.Find(ages, FindSamll18) ) |
FindLast |
从后向前查找第一个满足条件的元素 | 同Find |
查找最后一个能被 3 和 5 整除的数(Array.FindLast(ages, Find35) ) |
FindIndex |
从前向后查找第一个满足条件的元素的索引 | 同Find |
查找第一个能被 3 和 5 整除的数的索引(Array.FindIndex(ages, Find35) ) |
FindLastIndex |
从后向前查找第一个满足条件的元素的索引 | 同Find |
查找最后一个能被 3 和 5 整除的数的索引(Array.FindLastIndex(ages, Find35) ) |
TrueForAll |
判断数组所有元素 是否都满足条件(全满足返回true ) |
数组 + 回调函数 | 判断所有数字是否都是偶数(Array.TrueForAll(nums, FindEven) ) |
Exists |
判断数组至少有一个元素 满足条件(有一个满足返回true ) |
数组 + 回调函数 | 判断是否存在偶数(Array.Exists(nums, FindEven) ) |
ForEach |
遍历数组,对每个元素执行指定操作(替代for /foreach 的专用遍历) |
数组 + 回调函数(无返回值,执行操作) | 遍历数组并对每个元素 + 1 输出(Array.ForEach(nums, Each) ) |
2. 实例方法(数组对象.XXX)
实例方法通过数组对象调用,主要用于数组复制、元素操作、属性查询等。
方法 | 作用 | 示例 |
---|---|---|
CopyTo |
将当前数组元素复制到目标数组的指定位置 | str1.CopyTo(str2, 1) (将str1 从str2[1] 开始复制) |
GetLength |
获取多维数组指定维度的长度(一维数组等效于Length ) |
str1.GetLength(0) (返回一维数组str1 的长度) |
GetValue /SetValue |
获取 / 设置指定索引的元素值 | str1.GetValue(0) (获取str1[0] );str1.SetValue("郑爽", 1) (设置str1[1] ) |
Contains |
判断数组是否包含指定元素(需引入System.Linq ) |
str1.Contains("李云迪") (判断是否包含 "李云迪") |
3. 代码核心分析
-
高阶函数与回调函数 :
Find
/TrueForAll
等方法的第二个参数是 "回调函数"(如FindSamll18
),这些方法内部会循环数组,将每个元素传入回调函数,根据返回值判断是否满足条件。这种设计让数组操作更灵活(同一方法可通过不同回调实现不同查询)。 -
示例逻辑 : 例如
Array.Find(ages, FindSamll18)
的执行流程:循环ages
数组,每次将元素传入FindSamll18
,若返回true
(元素 < 18),则立即返回该元素。 代码中FindSamll18
/FindOdd
等回调函数,本质是将 "查询条件" 封装为可复用的逻辑。
二、Lambda 表达式(匿名函数的简化语法)
Lambda 表达式是匿名函数的语法糖,用于简化回调函数的定义,尤其在高阶函数中可大幅减少代码量。
1. 语法演进(从完整到简写)
Lambda 的核心是=>
(Lambda 运算符,读作 "goes to"),左侧为参数,右侧为函数体。语法逐步简化如下:
形式 | 示例代码 | 说明 |
---|---|---|
完整形式 | Func<int, int, int> fn1 = (int a, int b) => { return a + b; }; |
显式指定参数类型,函数体用{} 包裹,包含return |
省略参数类型 | Func<int, int, int> fn2 = (a, b) => { return a + b; }; |
编译器自动推断参数类型(基于委托类型Func<int, int, int> ) |
单个参数省略括号 | Func<int, int> fn3 = a => { return a + 13; }; |
仅一个参数时,可省略() |
单表达式省略{} 和return |
Func<int, int> fn4 = a => a + 13; |
函数体仅一个表达式时,自动返回表达式结果,无需{} 和return |
2. 在数组方法中的应用
Lambda 表达式最常用的场景是替代命名回调函数,让代码更紧凑。例如:
命名函数方式 | Lambda 表达式方式 | 说明 |
---|---|---|
Array.Find(ints, FindEven) |
Array.Find(ints, v => v % 2 == 0) |
用 Lambda 直接定义 "偶数条件",替代单独定义的FindEven 函数 |
Array.FindIndex(strings, FindLength2) |
Array.FindIndex(strings, v => v.Length == 2) |
直接在参数中定义 "长度为 2" 的条件,无需额外函数 |
3. 核心优势
-
简化代码:省去命名函数的定义,将条件逻辑直接写在调用处,减少跳转。
-
增强可读性 :查询条件与方法调用在同一位置,逻辑更连贯(如
v => v.StartsWith("吴")
直观表示 "以吴开头")。 -
灵活适配:针对一次性使用的简单条件,无需定义复用函数,降低冗余。
三、数组排序(冒泡排序、选择排序与Array.Sort
)
排序是数组操作的核心场景,这里介绍两种基础排序算法及Array.Sort
的用法。
1. 冒泡排序
原理:通过相邻元素的比较与交换,使最大元素逐步 "冒泡" 到数组尾部,重复缩小范围直至排序完成。
-
核心代码(优化版):
int[] ints1 = { 1, 8, 6, 2, 5, 4, 10, 3 }; for (int i = 0; i < ints1.Length; i++) { // 内层循环范围:0 ~ 长度-1-i(排除已排序的尾部元素) for (int j = 0; j < ints1.Length - 1 - i; j++) { if (ints1[j] > ints1[j + 1]) { (ints1[j], ints1[j + 1]) = (ints1[j + 1], ints1[j]); // 交换元素 } } }
-
优化点 :内层循环
j < ints1.Length - 1 - i
,每次排除已排序的尾部元素(减少无效比较)。 -
时间复杂度:O (n²)(适合小规模数据)。
2. 选择排序
原理:每次从剩余元素中找到最小值,与未排序部分的首个元素交换,逐步构建有序序列。
-
核心代码:
csfor (int i = 0; i < ints1.Length; i++) { int minIndex = i; // 记录最小值索引 for (int j = i + 1; j < ints1.Length; j++) { if (ints1[j] < ints1[minIndex]) minIndex = j; // 更新最小值索引 } if (minIndex != i) // 交换最小值到当前位置 { (ints1[i], ints1[minIndex]) = (ints1[minIndex], ints1[i]); } }
-
优势:交换次数少(每次外层循环仅交换 1 次),比冒泡排序略高效。
-
时间复杂度:O (n²)(同样适合小规模数据)。
3. Array.Sort
(内置排序)
C# 提供Array.Sort
静态方法,默认升序,支持通过 Lambda 自定义排序规则。
-
基础用法:
csint[] ints = { 3, 1, 4, 2 }; Array.Sort(ints); // 默认升序:[1, 2, 3, 4]
-
自定义排序(Lambda 比较器):
cs// 升序:a - b(返回负数时a在前,正数时b在前) Array.Sort(ints, (a, b) => a - b); // 降序:b - a Array.Sort(ints, (a, b) => b - a);
-
比较器逻辑:Lambda 返回值决定元素顺序 ------ 负数(a 在前)、正数(b 在前)、0(位置不变)。
4. 排序算法对比
维度 | 冒泡排序 | 选择排序 | Array.Sort (内置) |
---|---|---|---|
时间复杂度 | O(n²) | O(n²) | O (n log n)(快排 / 混合排序) |
交换次数 | 多(每次比较可能交换) | 少(每次外层循环最多交换 1 次) | 内部优化,效率高 |
适应性 | 可优化(检测到有序时提前终止) | 不可(固定遍历次数) | 自动适配数据规模 |
适用场景 | 小规模数据、教学演示 | 小规模数据、交换成本高的场景 | 实际开发(高效、简洁) |
四、作业实现与分析(People
数组查询)
基于People
数组的 5 个查询需求,结合前面的数组方法与 Lambda 表达式实现,分析如下:
1. 定义People
类与数组
cs
class People
{
public enum ESex { man, woman } // 性别枚举
public string Name { get; set; } // 姓名
public int Age { get; set; } // 年龄
public ESex Sex { get; set; } // 性别
}
// 初始化数组
People[] peoples = {
new People { Name="吴亦凡", Age=18, Sex=People.ESex.man },
new People { Name="郑爽", Age=22, Sex=People.ESex.woman },
new People { Name="李云迪", Age=21, Sex=People.ESex.man },
new People { Name="蔡徐坤", Age=32, Sex=People.ESex.man },
new People { Name="权志龙", Age=8, Sex=People.ESex.man },
new People { Name="杨幂", Age=18, Sex=People.ESex.woman }
};
2. 需求实现与分析
需求 | 实现代码 | 核心方法 / 逻辑 |
---|---|---|
1. 查询所有男性 | var allMen = peoples.Where(p => p.Sex == People.ESex.man).ToList(); |
Where (LINQ 方法):筛选所有满足 "性别为男性" 的元素,返回IEnumerable<People> ,通过ToList() 转为列表。 |
2. 查询第一个女性 | var firstWoman = Array.Find(peoples, p => p.Sex == People.ESex.woman); |
Array.Find :从前向后查找第一个 "性别为女性" 的元素。 |
3. 判断是否全为成年人(≥18) | bool allAdults = Array.TrueForAll(peoples, p => p.Age >= 18); |
TrueForAll :检查所有元素是否满足 "年龄≥18",因权志龙 8 岁,返回false 。 |
4. 计算年龄平均值 | double avgAge = peoples.Average(p => p.Age); |
Average (LINQ 方法):计算所有元素 "Age" 属性的平均值,结果为(18+22+21+32+8+18)/6 ≈ 19.83 。 |
5. 查询第一个未成年男性(<18) | var firstMinorMan = Array.Find(peoples, p => p.Sex == People.ESex.man && p.Age < 18); |
Array.Find :筛选 "男性且年龄 < 18" 的第一个元素(权志龙)。 |
3. 关键方法选择依据
-
批量查询(所有男性):用
Where
(返回所有满足条件的元素)。 -
单个元素查询(第一个女性 / 未成年男性):用
Array.Find
(高效返回首个匹配元素)。 -
全量判断(是否全为成年人):用
TrueForAll
(一次性验证所有元素)。 -
数值计算(平均年龄):用
Average
(LINQ 提供的聚合方法,简化计算)。