目录

第四章 数组与集合

4.1 数组

数组基础

数组是固定大小的、相同类型元素的连续集合。

// 声明和初始化
int[] numbers = new int[5];           // 声明长度为5的数组
int[] scores = { 85, 90, 78, 92, 88 }; // 声明并初始化
string[] names = new string[] { "Alice", "Bob", "Charlie" };
 
// 访问元素
Console.WriteLine(scores[0]);  // 85
scores[1] = 95;                // 修改元素
 
// 数组长度
Console.WriteLine(scores.Length);  // 5

多维数组

// 二维数组
int[,] matrix = new int[3, 4];
int[,] table = {
    { 1, 2, 3 },
    { 4, 5, 6 },
    { 7, 8, 9 }
};
 
// 访问
Console.WriteLine(table[0, 0]);  // 1
Console.WriteLine(table[1, 2]);  // 6
 
// 获取维度长度
Console.WriteLine(table.GetLength(0));  // 3行
Console.WriteLine(table.GetLength(1));  // 3列
 
// 遍历
for (int i = 0; i < table.GetLength(0); i++)
{
    for (int j = 0; j < table.GetLength(1); j++)
    {
        Console.Write($"{table[i, j]} ");
    }
    Console.WriteLine();
}

交错数组

// 交错数组(数组的数组)
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2 };
jagged[1] = new int[] { 3, 4, 5 };
jagged[2] = new int[] { 6 };
 
// 初始化语法
int[][] jagged2 = new int[][]
{
    new int[] { 1, 2 },
    new int[] { 3, 4, 5, 6 },
    new int[] { 7, 8, 9 }
};
 
// 遍历
foreach (int[] row in jagged)
{
    foreach (int num in row)
    {
        Console.Write(num + " ");
    }
    Console.WriteLine();
}

数组方法

int[] arr = { 3, 1, 4, 1, 5, 9, 2, 6 };
 
// 排序
Array.Sort(arr);  // [1, 1, 2, 3, 4, 5, 6, 9]
 
// 反转
Array.Reverse(arr);  // [9, 6, 5, 4, 3, 2, 1, 1]
 
// 查找
int index = Array.IndexOf(arr, 5);  // 返回5的索引
int lastIndex = Array.LastIndexOf(arr, 1);
 
// 二分查找(数组必须先排序)
int found = Array.BinarySearch(arr, 5);
 
// 清空
Array.Clear(arr, 0, 3);  // 将前3个元素设为0
 
// 复制
int[] copy = new int[arr.Length];
Array.Copy(arr, copy, arr.Length);
int[] clone = (int[])arr.Clone();

4.2 ArrayList与List<T>

ArrayList(非泛型)

using System.Collections;
 
ArrayList list = new ArrayList();
list.Add(1);           // 添加整数
list.Add("Hello");     // 添加字符串
list.Add(3.14);        // 添加浮点数
 
// 缺点:需要装箱拆箱,类型不安全
int num = (int)list[0];  // 需要强制转换

List<T>(泛型,推荐使用)

using System.Collections.Generic;
 
// 创建List
List<int> numbers = new List<int>();
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
 
// 添加元素
numbers.Add(10);
numbers.AddRange(new[] { 20, 30, 40 });
 
// 插入
numbers.Insert(0, 5);  // 在索引0处插入5
 
// 删除
numbers.Remove(10);       // 删除第一个值为10的元素
numbers.RemoveAt(0);      // 删除索引0的元素
numbers.RemoveAll(x => x > 25);  // 删除所有大于25的元素
 
// 查找
bool contains = numbers.Contains(20);
int index = numbers.IndexOf(20);
int foundIndex = numbers.FindIndex(x => x > 15);
int found = numbers.Find(x => x > 15);
 
// 排序和反转
numbers.Sort();
numbers.Sort((a, b) => b.CompareTo(a));  // 降序排序
numbers.Reverse();
 
// 其他操作
numbers.Clear();
int count = numbers.Count;
int capacity = numbers.Capacity;

4.3 Dictionary<K,V>

// 创建字典
Dictionary<string, int> ages = new Dictionary<string, int>();
Dictionary<string, string> capitals = new Dictionary<string, string>
{
    { "中国", "北京" },
    { "美国", "华盛顿" },
    { "日本", "东京" }
};
 
// 添加
ages.Add("Alice", 25);
ages["Bob"] = 30;  // 添加或更新
 
// 安全添加
if (!ages.ContainsKey("Charlie"))
{
    ages.Add("Charlie", 35);
}
 
// 访问
int aliceAge = ages["Alice"];
 
// 安全访问
if (ages.TryGetValue("Alice", out int age))
{
    Console.WriteLine($"Alice的年龄: {age}");
}
 
// 遍历
foreach (var pair in capitals)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}
 
foreach (string key in capitals.Keys)
{
    Console.WriteLine(key);
}
 
foreach (string value in capitals.Values)
{
    Console.WriteLine(value);
}
 
// 删除
ages.Remove("Alice");
ages.Clear();

4.4 其他常用集合

HashSet<T>

// 集合:无序、不重复
HashSet<int> set = new HashSet<int>();
set.Add(1);
set.Add(2);
set.Add(1);  // 重复,不会添加
 
Console.WriteLine(set.Count);  // 2
 
// 集合运算
HashSet<int> set1 = new HashSet<int> { 1, 2, 3, 4 };
HashSet<int> set2 = new HashSet<int> { 3, 4, 5, 6 };
 
set1.IntersectWith(set2);  // 交集: {3, 4}
set1.UnionWith(set2);      // 并集
set1.ExceptWith(set2);     // 差集
set1.SymmetricExceptWith(set2);  // 对称差集

Queue<T>

// 队列:先进先出(FIFO)
Queue<string> queue = new Queue<string>();
queue.Enqueue("First");   // 入队
queue.Enqueue("Second");
queue.Enqueue("Third");
 
string first = queue.Dequeue();  // 出队: "First"
string peek = queue.Peek();      // 查看队首但不移除: "Second"
int count = queue.Count;

Stack<T>

// 栈:后进先出(LIFO)
Stack<string> stack = new Stack<string>();
stack.Push("First");   // 压栈
stack.Push("Second");
stack.Push("Third");
 
string top = stack.Pop();    // 出栈: "Third"
string peek = stack.Peek();  // 查看栈顶: "Second"
int count = stack.Count;

SortedList<TKey, TValue>

// 按键排序的字典
SortedList<string, int> sortedList = new SortedList<string, int>();
sortedList.Add("Charlie", 3);
sortedList.Add("Alice", 1);
sortedList.Add("Bob", 2);
 
// 遍历时按键排序
foreach (var pair in sortedList)
{
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}
// 输出: Alice, Bob, Charlie

4.5 LINQ基础

LINQ简介

LINQ(Language Integrated Query)是集成在C#中的查询语言。

using System.Linq;
 
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

查询语法

// 查询语法
var evenNumbers = from n in numbers
                  where n % 2 == 0
                  select n;
 
// 排序
var sorted = from n in numbers
             orderby n descending
             select n;
 
// 多条件
var result = from n in numbers
             where n > 3 && n < 8
             orderby n
             select n;

方法语法

// Where过滤
var evenNumbers = numbers.Where(n => n % 2 == 0);
 
// Select投影
var squares = numbers.Select(n => n * n);
 
// 排序
var sorted = numbers.OrderBy(n => n);
var sortedDesc = numbers.OrderByDescending(n => n);
 
// 多级排序
var students = new[]
{
    new { Name = "Alice", Grade = 90, Class = "A" },
    new { Name = "Bob", Grade = 85, Class = "B" },
    new { Name = "Charlie", Grade = 90, Class = "A" }
};
 
var ordered = students
    .OrderBy(s => s.Class)
    .ThenByDescending(s => s.Grade);

常用LINQ操作

int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 
// 聚合
int sum = nums.Sum();
double avg = nums.Average();
int max = nums.Max();
int min = nums.Min();
int count = nums.Count();
int countEven = nums.Count(n => n % 2 == 0);
 
// 量化
bool hasAny = nums.Any();
bool hasEven = nums.Any(n => n % 2 == 0);
bool allPositive = nums.All(n => n > 0);
bool containsFive = nums.Contains(5);
 
// 分区
var first3 = nums.Take(3);        // 前3个
var skip3 = nums.Skip(3);         // 跳过前3个
var takeWhile = nums.TakeWhile(n => n < 5);
 
// 去重
var distinct = new[] { 1, 2, 2, 3, 3, 3 }.Distinct();
 
// 转换
List<int> list = nums.ToList();
int[] array = list.ToArray();
Dictionary<int, int> dict = nums.ToDictionary(n => n, n => n * n);

4.6 Span<T>和Memory<T>(C# 7.2+)

// Span<T>: 安全的内存切片,栈上分配
void ProcessSpan()
{
    int[] arr = { 1, 2, 3, 4, 5 };
    Span<int> span = arr;           // 整个数组
    Span<int> slice = arr.AsSpan(1, 3);  // 切片 [2, 3, 4]
 
    span[0] = 100;  // 修改会影响原数组
 
    // 栈上分配的Span
    Span<int> stackSpan = stackalloc int[10];
}
 
// Memory<T>: 堆上分配,可用于异步操作
async Task ProcessMemoryAsync()
{
    byte[] data = await File.ReadAllBytesAsync("file.txt");
    Memory<byte> memory = data;
    Memory<byte> slice = memory.Slice(10, 100);
}

4.7 集合初始化器

// 集合初始化器
var list = new List<int> { 1, 2, 3, 4, 5 };
 
var dict = new Dictionary<string, int>
{
    ["one"] = 1,
    ["two"] = 2,
    ["three"] = 3
};
 
// 对象集合
var people = new List<Person>
{
    new Person { Name = "Alice", Age = 25 },
    new Person { Name = "Bob", Age = 30 }
};
 
// C# 6+ 索引初始化器
var numbers = new Dictionary<int, string>
{
    [1] = "one",
    [2] = "two",
    [3] = "three"
};

练习题

基础练习

1. 数组操作:创建一个整数数组,实现以下功能:

  1. 找出最大值和最小值
  2. 计算平均值
  3. 找出第二大的数
  4. 将数组按升序排序后反转

2. 矩阵运算:编写方法实现两个二维矩阵的加法和乘法。

3. List操作:创建一个字符串List,完成以下操作:

  1. 添加10个名字
  2. 删除所有以“A”开头的名字
  3. 按字母顺序排序
  4. 查找包含字母“e”的所有名字

进阶练习

4. 学生成绩系统:使用Dictionary存储学生姓名和成绩,实现:

  1. 添加学生成绩
  2. 查询学生成绩
  3. 计算班级平均分
  4. 找出最高分和最低分的学生
  5. 按成绩排序输出

5. 集合运算:给定两个整数数组,找出:

  1. 同时存在于两个数组中的元素(交集)
  2. 只存在于第一个数组中的元素(差集)
  3. 存在于任一数组中的所有不同元素(并集)

6. 队列实现:使用Queue实现一个任务调度器,支持:

  1. 添加任务
  2. 执行任务(出队)
  3. 查看待处理任务数
  4. 清空所有任务

挑战练习

7. LINQ综合应用:给定一个Product类(Id, Name, Category, Price),使用LINQ实现:

  1. 按类别分组统计数量和平均价格
  2. 找出每个类别中最贵的产品
  3. 筛选价格高于平均价的产品
  4. 实现分页查询

8. 自定义集合:实现一个简单的泛型集合类MyList<T>,支持:

  1. 添加元素
  2. 索引访问
  3. 枚举器(实现IEnumerable<T>)
  4. 自动扩容

9. 数据统计:使用数组或List存储一组实验数据,实现:

  1. 计算均值、中位数、众数
  2. 计算方差和标准差
  3. 找出异常值(超过3倍标准差的数据点)

本章小结

本章学习了C#的数组和集合:

  1. 一维数组、多维数组和交错数组
  2. Array类的方法和操作
  3. 泛型集合List<T>、Dictionary<K,V>
  4. 专用集合HashSet、Queue、Stack
  5. LINQ的基础查询语法和方法语法
  6. Span<T>和Memory<T>的高效内存操作

选择合适的集合类型对于程序性能至关重要。