目录
[一、手动实现 Array.Find 功能](#一、手动实现 Array.Find 功能)
[1. 原理说明](#1. 原理说明)
[2. 完整代码](#2. 完整代码)
[3. 核心逻辑总结](#3. 核心逻辑总结)
[1. 算法思想](#1. 算法思想)
[2. 代码实现(基础版 + 优化版)](#2. 代码实现(基础版 + 优化版))
[3. 要点](#3. 要点)
[1. 算法思想](#1. 算法思想)
[2. 原生选择排序代码](#2. 原生选择排序代码)
[3. 结合 Lambda 使用 Array.Sort(官方排序)](#3. 结合 Lambda 使用 Array.Sort(官方排序))
[四、非泛型集合 ArrayList](#四、非泛型集合 ArrayList)
[1. 基本介绍](#1. 基本介绍)
[装箱 & 拆箱](#装箱 & 拆箱)
[2. 常用方法与完整示例](#2. 常用方法与完整示例)
[3. 缺点总结](#3. 缺点总结)
[五、泛型集合 List](#五、泛型集合 List)
[1. 基本介绍](#1. 基本介绍)
[优势(对比 ArrayList)](#优势(对比 ArrayList))
[2. 常用方法 & 代码示例](#2. 常用方法 & 代码示例)
[3. 开发建议](#3. 开发建议)
[六、字典 Dictionary](#六、字典 Dictionary)
[1. 基本介绍](#1. 基本介绍)
[2. 常用 API](#2. 常用 API)
[3. 完整代码示例](#3. 完整代码示例)
[1. 数组 vs ArrayList vs List](#1. 数组 vs ArrayList vs List)
[2. 两大排序对比](#2. 两大排序对比)
[3. 集合使用优先级(开发规范)](#3. 集合使用优先级(开发规范))
一、手动实现 Array.Find 功能
1. 原理说明
Array.Find 本质:遍历数组,依次把元素传入条件委托,匹配成功立即返回当前元素;遍历结束无匹配,返回类型默认值。
- 委托:
Func<int, bool>接收一个 int 参数、返回 bool(匹配条件) - 健壮性处理:判断数组、委托是否为
null,主动抛出参数空异常,和官方Array行为保持一致。
2. 完整代码
cs
using System;
namespace _2自己实现find功能
{
internal class Program
{
static void Main(string[] args)
{
int[] ages = new int[] { 1, 2, 3 };
// 查询第一个偶数
Console.WriteLine(MyArray.Find(ages, v => v % 2 == 0));
// 查询第一个能被3整除的数
Console.WriteLine(MyArray.Find(ages, v => v % 3 == 0));
}
}
/// <summary>
/// 自定义数组工具类,复刻官方Find方法
/// </summary>
public class MyArray
{
/// <summary>
/// 查找数组中第一个满足条件的元素
/// </summary>
/// <param name="arr">源数组</param>
/// <param name="match">匹配条件委托</param>
/// <returns>匹配元素,无匹配返回int默认值0</returns>
public static int Find(int[] arr, Func<int, bool> match)
{
// 空校验
if (arr == null)
{
throw new ArgumentNullException("array");
}
if (match == null)
{
throw new ArgumentNullException("match");
}
// 遍历数组
for (int i = 0; i < arr.Length; i++)
{
// 执行匹配条件
if (match(arr[i]))
{
return arr[i];
}
}
// 遍历完毕未找到,返回默认值
return 0;
}
}
}
3. 核心逻辑总结
- 先做空判断,防止程序崩溃;
for循环遍历数组每一个元素;- 调用传入的
Func委托判断是否满足条件; - 找到第一个匹配项直接
return; - 全遍历无匹配,返回值类型默认值。
二、冒泡排序
1. 算法思想
相邻两个元素两两比较,前 > 后 则交换位置,每一轮遍历都会把当前最大值 "冒泡" 到数组末尾。
- 外层循环:控制排序总轮数
- 内层循环:控制每一轮相邻元素比较次数
- 优化点:每一轮排序后,末尾已有序元素无需再比较,内层循环次数递减。
2. 代码实现(基础版 + 优化版)
cs
using System;
namespace _3冒泡排序
{
internal class Program
{
static void Main(string[] args)
{
int[] num1 = { 1, 2, 3, 4, 57, 8, 8, 9 };
// 优化版冒泡排序(推荐)
for (int i = 0; i < num1.Length; i++)
{
// -i:跳过后面已经排好序的元素
for (int j = 0; j < num1.Length - 1 - i; j++)
{
// 前一个数 > 后一个数,交换位置(升序)
if (num1[j] > num1[j + 1])
{
(num1[j], num1[j + 1]) = (num1[j + 1], num1[j]);
}
}
}
Console.WriteLine(string.Join("-", num1));
}
}
}
3. 要点
- 升序:
num1[j] > num1[j+1]交换 - 降序:
num1[j] < num1[j+1]交换 num1.Length - 1 - i:核心优化,减少无效比较。
三、选择排序
1. 算法思想
每一轮先找到最小值的索引,一轮结束后,将最小值和当前起始位置元素交换。
- 外层循环:确定每一轮的起始位置
- 内层循环:从起始位置向后遍历,记录最小值下标
- 一轮结束:如果最小值下标!= 起始下标,执行交换。
2. 原生选择排序代码
cs
using System;
namespace _4选择排序
{
internal class Program
{
static void Main(string[] args)
{
int[] nums = { 1, 2, 30, 4, 5, 8, 8, 8, 9 };
// 原生选择排序(升序)
for (int i = 0; i < nums.Length; i++)
{
int minIndex = i; // 假设当前下标为最小值下标
// 从i下一位开始查找更小值
for (int j = i + 1; j < nums.Length; j++)
{
if (nums[j] < nums[minIndex])
{
minIndex = j; // 更新最小值下标
}
}
// 最小值不在当前位置,执行交换
if (minIndex != i)
{
(nums[i], nums[minIndex]) = (nums[minIndex], nums[i]);
}
}
Console.WriteLine(string.Join("-", nums));
}
}
}
3. 结合 Lambda 使用 Array.Sort(官方排序)
C# 内置 Array.Sort 支持传入比较委托,配合 Lambda 快速实现升降序:
cs
int[] nums = { 1, 2, 30, 4, 5, 8, 8, 8, 9 };
// 1. 升序
Array.Sort(nums, (x, y) => x - y);
Console.WriteLine(string.Join("-", nums));
// 2. 降序
Array.Sort(nums, (x, y) => y - x);
Console.WriteLine(string.Join("-", nums));
// 3. 完整判断写法(可读性更强)
Array.Sort(nums, (x, y) =>
{
if (x < y) return -1;
if (x > y) return 1;
return 0;
});
规则:
- 返回 负数:x 排在 y 前面
- 返回 正数:x 排在 y 后面
- 返回 0:位置不变
四、非泛型集合 ArrayList
1. 基本介绍
命名空间:using System.Collections; ArrayList 是动态数组 ,长度可自动扩容,弥补普通数组长度固定的缺陷。
核心特点(和普通数组对比)
- 数组:长度固定,类型安全,无装箱拆箱;
- ArrayList:长度动态可变,存储 object 类型 ,类型不安全,存在装箱、拆箱;
- 可存放任意类型数据(int、string、bool、对象等)。
装箱 & 拆箱
- 装箱:值类型 → 引用类型(存入 ArrayList 时自动发生)
- 拆箱:引用类型 → 值类型(取出强转时发生)
2. 常用方法与完整示例
cs
using System;
using System.Collections;
namespace _5ArrayList动态集合
{
internal class Program
{
static void Main(string[] args)
{
int[] ages = new int[] { 1, 2, 3, 4, 5 };
ArrayList list1 = new ArrayList();
// 1. Add:添加单个元素(装箱)
list1.Add(1);
list1.Add(2);
list1.Add(3);
// 2. 取值 + 拆箱
Console.WriteLine((int)list1[0]);
// 3. AddRange:批量添加数组/集合
list1.AddRange(ages);
// 4. Contains:判断是否包含元素
Console.WriteLine(list1.Contains(1));
// 5. IndexOf:获取元素索引
Console.WriteLine(list1.IndexOf(1));
// 6. Remove / RemoveAt:删除元素
list1.Remove(1); // 根据元素删除
list1.RemoveAt(0); // 根据索引删除
// 7. 修改元素
list1[0] = "张三";
// 8. Insert / InsertRange:插入元素
list1.Insert(0, "李四");
list1.InsertRange(2, ages);
// 9. LastIndexOf:从后查找索引
Console.WriteLine(list1.LastIndexOf("李四"));
// 10. GetRange:截取子集(视图,非独立集合)
ArrayList list2 = list1.GetRange(0, 3);
// 遍历集合
foreach (var item in list1)
{
Console.WriteLine(item + "------");
}
}
}
}
3. 缺点总结
- 类型不安全,编译不校验类型;
- 频繁装箱拆箱,性能较差;
- 现代开发基本被 泛型 List<T> 替代。
五、泛型集合 List<T>
1. 基本介绍
命名空间:using System.Collections.Generic; List<T> 是 泛型动态集合 ,T 代表指定存储类型。
优势(对比 ArrayList)
- 类型安全:编译时强制校验类型,不能存入其他类型;
- 无装箱拆箱,执行效率高;
- 语法简洁,兼容数组 + ArrayList 所有常用操作。
2. 常用方法 & 代码示例
cs
using System;
using System.Collections.Generic;
namespace _6泛型集合
{
internal class Program
{
static void Main(string[] args)
{
// 定义泛型集合,只能存 string
List<string> list1 = new List<string> { "张三", "李四" };
Console.WriteLine("元素总数:" + list1.Count);
// 1. Add / AddRange 添加
list1.Add("1");
list1.AddRange(new List<string> { "王五" });
// 2. Remove / RemoveAt / RemoveAll 删除
// list1.Remove("1");
// list1.RemoveAt(0);
// 按条件删除,返回删除个数
Console.WriteLine(list1.RemoveAll(v => v.StartsWith("李")));
// 3. 索引修改
list1[2] = "焦恩俊";
// 4. Insert 插入
list1.Insert(4, "何家劲");
// 5. ForEach 遍历(配合Lambda)
list1.ForEach(x => Console.WriteLine(x));
// 普通foreach遍历
foreach (var item in list1)
{
Console.WriteLine(item + "-------");
}
}
}
}
3. 开发建议
日常开发优先使用 List<T>,不再使用 ArrayList。
六、字典 Dictionary<TKey, TValue>
1. 基本介绍
命名空间:using System.Collections.Generic; 字典是键值对集合 :Key(键) → Value(值)
- Key:唯一不重复,可自定义类型(int、string 等)
- Value:存储对应数据,可重复
- 类比:加强版数组,下标不再局限于 int。
2. 常用 API
Add(Key, Value):添加键值对Remove(Key):根据键删除ContainsKey(Key):判断键是否存在Count:获取键值对总数Keys:获取所有键集合Values:获取所有值集合
3. 完整代码示例
cs
using System;
using System.Collections.Generic;
namespace _7字典
{
internal class Program
{
static void Main(string[] args)
{
// 键:int 值:string
Dictionary<int, string> dic = new Dictionary<int, string>();
// 1. 添加键值对
dic.Add(0, "何润东");
dic.Add(1, "周传雄-情歌教父");
// 2. 根据键取值
Console.WriteLine(dic[0]);
// 3. 删除指定键
dic.Remove(0);
// 4. 总数 & 判断键是否存在
Console.WriteLine("总数:" + dic.Count);
Console.WriteLine("是否包含键0:" + dic.ContainsKey(0));
// ========== 三种遍历方式 ==========
// 遍历所有键
foreach (var key in dic.Keys)
{
Console.WriteLine(key);
}
// 遍历所有值
foreach (var val in dic.Values)
{
Console.WriteLine(val);
}
// 遍历整个键值对(最常用)
foreach (var item in dic)
{
Console.WriteLine($"键:{item.Key} 值:{item.Value}");
}
// 嵌套字典示例:键=班级名,值=学员数组
Dictionary<string, string[]> dic1 = new Dictionary<string, string[]>()
{
{"一班", new string[]{ "马化腾","马云","马斯克"} },
{"二班", new string[]{ "焦恩俊","严屹宽","霍建华","乔振宇"} }
};
string[] arr = dic1["一班"];
Console.WriteLine(string.Join("-", arr));
}
}
}
七、全套知识点汇总对比
1. 数组 vs ArrayList vs List<T>
| 类型 | 长度 | 类型安全 | 装箱拆箱 | 适用场景 |
|---|---|---|---|---|
| 普通数组 | 固定 | 安全 | 无 | 长度固定、高性能场景 |
| ArrayList | 动态 | 不安全 | 有 | 旧项目、兼容老代码 |
| List<T> | 动态 | 安全 | 无 | 现代开发首选 |
2. 两大排序对比
- 冒泡排序:相邻比较、交换频繁,逻辑简单,适合小规模数据;
- 选择排序:先找最值再交换,交换次数少,效率略优于冒泡。
3. 集合使用优先级(开发规范)
- 固定长度、同类型数据 → 数组
- 动态长度、同类型数据 → List<T>
- 键值映射、一对一查询 → Dictionary<TKey,TValue>
- 绝不主动使用
ArrayList(仅做历史了解)