存放数据的地方 - 数组与集合

21 阅读5分钟

专栏导航

单个变量只能存储一个数据。数组让我们可以像收纳盒一样,整齐地存储多个相同类型的数据。

什么是数组?

现实生活中的类比

想象一个班级的花名册:

座位号:1   2   3   4   5
学生:小明 小红 小刚 小丽 小伟
  • 花名册 = 数组
  • 每个座位 = 数组的元素
  • 座位号 = 数组的索引(下标)

为什么需要数组?

不使用数组(存储多个学生姓名):

string student1 = "小明";
string student2 = "小红";
string student3 = "小刚";
string student4 = "小丽";
string student5 = "小伟";
// ... 如果有 50 个学生呢?

使用数组:

string[] students = { "小明", "小红", "小刚", "小丽", "小伟" };

数组的特点

✅ 相同类型:所有元素必须是相同的数据类型
✅ 固定长度:创建后长度不能改变
✅ 连续存储:元素在内存中连续存放
✅ 索引访问:通过索引(从0开始)快速访问元素

声明和初始化数组

方式一:声明并初始化

// 完整语法
类型[] 数组名 = new 类型[长度];

示例:

// 声明一个长度为 5 的整数数组
int[] numbers = new int[5];

// 声明一个长度为 3 的字符串数组
string[] names = new string[3];

方式二:声明并直接赋值

int[] numbers = new int[] { 1, 2, 3, 4, 5 };
string[] names = { "张三", "李四", "王五" };  // 简化写法

数组的索引

数组的索引从 0 开始,最后一个元素的索引是 长度 - 1

string[] names = { "张三", "李四", "王五", "赵六", "孙七" };
// 索引:     0      1      2      3      4

访问数组元素:

string[] names = { "张三", "李四", "王五", "赵六", "孙七" };

Console.WriteLine(names[0]);  // 输出:张三
Console.WriteLine(names[2]);  // 输出:王五
Console.WriteLine(names[4]);  // 输出:孙七

修改数组元素:

int[] numbers = { 1, 2, 3, 4, 5 };
numbers[2] = 100;  // 修改索引为 2 的元素

Console.WriteLine(numbers[2]);  // 输出:100

数组的属性

获取数组长度

使用 Length 属性获取数组的长度(元素个数)。

int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine($"数组长度:{numbers.Length}");  // 输出:数组长度:5

遍历数组

int[] numbers = { 1, 2, 3, 4, 5 };

// 方式一:使用 for 循环
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine(numbers[i]);
}

// 方式二:使用 foreach 循环(推荐)
foreach (int num in numbers)
{
    Console.WriteLine(num);
}

数组常见操作

存储一组学生姓名

using System;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建字符串数组存储学生姓名
            string[] students = { "张三", "李四", "王五", "赵六", "孙七" };

            Console.WriteLine("=== 班级花名册 ===");
            Console.WriteLine($"班级人数:{students.Length}");
            Console.WriteLine();

            // 遍历输出所有学生
            for (int i = 0; i < students.Length; i++)
            {
                Console.WriteLine($"学号{i + 1}{students[i]}");
            }

            Console.WriteLine();
            Console.WriteLine("=== 使用 foreach 遍历 ===");
            foreach (string student in students)
            {
                Console.WriteLine($"学生:{student}");
            }
        }
    }
}

输出:

=== 班级花名册 ===
班级人数:5

学号1:张三
学号2:李四
学号3:王五
学号4:赵六
学号5:孙七

=== 使用 foreach 遍历 ===
学生:张三
学生:李四
学生:王五
学生:赵六
学生:孙七

统计数组最大值

using System;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = { 23, 45, 12, 67, 34, 89, 56 };

            Console.WriteLine("数组元素:");
            foreach (int num in numbers)
            {
                Console.Write(num + " ");
            }
            Console.WriteLine();

            // 找最大值
            int max = numbers[0];  // 假设第一个元素是最大的

            for (int i = 1; i < numbers.Length; i++)
            {
                if (numbers[i] > max)
                {
                    max = numbers[i];
                }
            }

            Console.WriteLine($"最大值:{max}");
        }
    }
}

输出:

数组元素:
23 45 12 67 34 89 56 
最大值:89

统计数组最小值、平均值

int[] numbers = { 23, 45, 12, 67, 34, 89, 56 };

// 最大值
int max = numbers[0];
// 最小值
int min = numbers[0];
// 总和
int sum = 0;

for (int i = 0; i < numbers.Length; i++)
{
    if (numbers[i] > max)
        max = numbers[i];
    if (numbers[i] < min)
        min = numbers[i];
    sum += numbers[i];
}

double average = (double)sum / numbers.Length;

Console.WriteLine($"最大值:{max}");
Console.WriteLine($"最小值:{min}");
Console.WriteLine($"平均值:{average:F2}");

输出:

最大值:89
最小值:12
平均值:46.57

使用 LINQ 快速计算(推荐!)

对于数组或 List<T>,可以使用 LINQ 提供的扩展方法快速进行统计计算,大大简化代码!

using System;
using System.Linq;  // 引入 LINQ 命名空间

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = { 23, 45, 12, 67, 34, 89, 56 };

            // LINQ 扩展方法自动完成计算
            int sum = numbers.Sum();      // 求和:326
            int max = numbers.Max();      // 最大值:89
            int min = numbers.Min();      // 最小值:12
            double avg = numbers.Average();  // 平均值:46.571...

            Console.WriteLine($"数组元素:{string.Join(", ", numbers)}");
            Console.WriteLine($"总和:{sum}");
            Console.WriteLine($"最大值:{max}");
            Console.WriteLine($"最小值:{min}");
            Console.WriteLine($"平均值:{avg:F2}");
        }
    }
}

输出:

数组元素:23, 45, 12, 67, 34, 89, 56
总和:326
最大值:89
最小值:12
平均值:46.57
LINQ 扩展方法一览
方法功能返回类型
Sum()计算元素总和int/double 等
Max()获取最大值元素类型
Min()获取最小值元素类型
Average()计算平均值double
注意事项
  1. 必须引入命名空间using System.Linq;
  2. 适用于数组int[] a = { 1, 2, 3 }; a.Sum();
  3. 适用于 ListList<int> list = new List<int> { 1, 2, 3 }; list.Sum();
  4. 空集合:对空集合调用这些方法会抛出异常,使用前应检查:list.Count > 0 ? list.Sum() : 0;
对比:传统方式 vs LINQ
int[] numbers = { 1, 2, 3, 4, 5 };

// 传统方式(需要 10+ 行代码)
int sum = 0;
for (int i = 0; i < numbers.Length; i++)
{
    sum += numbers[i];
}

// LINQ 方式(1 行代码搞定!)
int sum = numbers.Sum();

数组排序

排序是数组操作中的常见需求。C# 提供了多种排序方式。

使用 Array.Sort() 方法(数组原地排序)

Array.Sort() 会对数组进行原地排序(直接修改原数组)。

using System;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = { 23, 45, 12, 67, 34, 89, 56 };

            Console.WriteLine("排序前:");
            Console.WriteLine(string.Join(", ", numbers));

            // 原地升序排序(直接修改原数组)
            Array.Sort(numbers);

            Console.WriteLine("\n升序排序后:");
            Console.WriteLine(string.Join(", ", numbers));

            // 原地降序排序(先升序,再反转)
            Array.Reverse(numbers);

            Console.WriteLine("\n降序排序后:");
            Console.WriteLine(string.Join(", ", numbers));
        }
    }
}

输出:

排序前:
23, 45, 12, 67, 34, 89, 56

升序排序后:
12, 23, 34, 45, 56, 67, 89

降序排序后:
89, 67, 56, 45, 34, 23, 12
使用 LINQ OrderBy() 方法(返回新数组)

LINQ 的排序方法不会修改原数组,而是返回一个新的已排序数组。

using System;
using System.Linq;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = { 23, 45, 12, 67, 34, 89, 56 };

            Console.WriteLine("原数组:");
            Console.WriteLine(string.Join(", ", numbers));

            // 升序排序(返回新数组)
            int[] ascSorted = numbers.OrderBy(x => x).ToArray();

            Console.WriteLine("\n升序排序(新数组):");
            Console.WriteLine(string.Join(", ", ascSorted));

            // 降序排序
            int[] descSorted = numbers.OrderByDescending(x => x).ToArray();

            Console.WriteLine("\n降序排序(新数组):");
            Console.WriteLine(string.Join(", ", descSorted));

            Console.WriteLine("\n原数组保持不变:");
            Console.WriteLine(string.Join(", ", numbers));
        }
    }
}

输出:

原数组:
23, 45, 12, 67, 34, 89, 56

升序排序(新数组):
12, 23, 34, 45, 56, 67, 89

降序排序(新数组):
89, 67, 56, 45, 34, 23, 12

原数组保持不变:
23, 45, 12, 67, 34, 89, 56
字符串数组排序
using System;
using System.Linq;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] names = { "张三", "李四", "王五", "赵六", "孙七" };

            Console.WriteLine("排序前:");
            Console.WriteLine(string.Join(", ", names));

            // 按字母顺序排序
            string[] sortedNames = names.OrderBy(x => x).ToArray();

            Console.WriteLine("\n排序后:");
            Console.WriteLine(string.Join(", ", sortedNames));

            // 按字符串长度排序
            string[] lengthSorted = names.OrderBy(x => x.Length).ToArray();

            Console.WriteLine("\n按长度排序:");
            Console.WriteLine(string.Join(", ", lengthSorted));
        }
    }
}

输出:

排序前:
张三, 李四, 王五, 赵六, 孙七

排序后:
李四, 孙七, 王五, 张三, 赵六

按长度排序:
张三, 李四, 王五, 孙七, 赵六
排序方法对比
方法是否修改原数组返回值适用场景
Array.Sort(arr)✅ 是void需要原地排序,节省内存
arr.OrderBy(x => x)❌ 否IOrderedEnumerable<T>需要保留原数组
arr.OrderByDescending(x => x)❌ 否IOrderedEnumerable<T>降序排序
Array.Reverse(arr)✅ 是void反转数组
实践:学生成绩排序
using System;
using System.Linq;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            double[] scores = { 85.5, 92.0, 78.5, 90.0, 65.0, 88.5 };

            Console.WriteLine("原始成绩:");
            Console.WriteLine(string.Join(", ", scores));

            // 升序排序
            var ascScores = scores.OrderBy(x => x).ToArray();
            Console.WriteLine($"\n最低分:{ascScores.First()}");  // 65.0
            Console.WriteLine($"最高分:{ascScores.Last()}");    // 92.0

            // 降序排序(方便排名)
            var descScores = scores.OrderByDescending(x => x).ToArray();
            Console.WriteLine("\n成绩排名:");
            for (int i = 0; i < descScores.Length; i++)
            {
                Console.WriteLine($"第{i + 1}名:{descScores[i]}");
            }
        }
    }
}

输出:

原始成绩:
85.5, 92, 78.5, 90, 65, 88.5

最低分:65
最高分:92

成绩排名:
第1名:92
第2名:90
第3名:88.5
第4名:85.5
第5名:78.5
第6名:65

数组的常见错误

索引越界

int[] numbers = { 1, 2, 3, 4, 5 };
// 索引: 0, 1, 2, 3, 4

Console.WriteLine(numbers[5]);  // 错误!索引越界

错误信息:

IndexOutOfRangeException: Index was outside the bounds of the array.

正确做法:

if (index >= 0 && index < numbers.Length)
{
    Console.WriteLine(numbers[index]);
}
else
{
    Console.WriteLine("索引越界!");
}

数组未初始化

int[] numbers;
Console.WriteLine(numbers[0]);  // 错误!未初始化

正确做法:

int[] numbers = new int[5];  // 初始化数组
Console.WriteLine(numbers[0]);  // 输出:0(默认值)

数组默认值

创建数组时,元素会有默认值:

类型默认值
int0
double0.0
boolfalse
stringnull
int[] numbers = new int[3];
Console.WriteLine(numbers[0]);  // 输出:0

string[] names = new string[3];
Console.WriteLine(names[0] ?? "空");  // 输出:空

List 简介(动态数组)

数组长度固定,而 List<T> 可以动态增删元素,更灵活。

创建 List

using System.Collections.Generic;  // 需要引入命名空间

// 创建空的整数列表
List<int> numbers = new List<int>();

// 创建带初始元素的列表
List<string> names = new List<string> { "张三", "李四", "王五" };

List 常用操作

List<string> students = new List<string>();

// 添加元素
students.Add("张三");
students.Add("李四");
students.Add("王五");

// 访问元素
Console.WriteLine(students[0]);  // 输出:张三

// 修改元素
students[0] = "小明";

// 删除元素
students.Remove("李四");  // 删除指定值
students.RemoveAt(0);     // 删除指定索引

// 获取元素数量
Console.WriteLine($"学生数量:{students.Count}");  // Count,不是 Length

// 遍历
foreach (string student in students)
{
    Console.WriteLine(student);
}

// 查找元素
bool exists = students.Contains("王五");  // true

// 清空所有元素
students.Clear();

数组 vs List

特性数组List
长度固定动态
添加/删除不支持支持
访问速度
内存占用稍大
适用场景长度固定、频繁访问频繁增删

使用 List 存储学生成绩

using System;
using System.Collections.Generic;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            List<double> scores = new List<double>();

            Console.WriteLine("===== 学生成绩管理 =====");

            // 添加成绩
            scores.Add(85.5);
            scores.Add(92.0);
            scores.Add(78.5);
            scores.Add(90.0);

            Console.WriteLine($"已添加 {scores.Count} 个成绩:");
            foreach (double score in scores)
            {
                Console.WriteLine(score);
            }

            // 计算平均分
            double sum = 0;
            foreach (double score in scores)
            {
                sum += score;
            }
            double average = sum / scores.Count;
            Console.WriteLine($"\n平均分:{average:F2}");

            // 查找最高分
            double maxScore = scores[0];
            foreach (double score in scores)
            {
                if (score > maxScore)
                    maxScore = score;
            }
            Console.WriteLine($"最高分:{maxScore}");

            // 删除低于 80 分的成绩
            scores.RemoveAll(s => s < 80);
            Console.WriteLine($"\n删除低分后剩余:{scores.Count} 个");
            foreach (double score in scores)
            {
                Console.WriteLine(score);
            }
        }
    }
}

输出:

===== 学生成绩管理 =====
已添加 4 个成绩:
85.5
92
78.5
90

平均分:86.50
最高分:92

删除低分后剩余:3 个
85.5
92
90

实践:购物清单管理

using System;
using System.Collections.Generic;

namespace Week5Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> shoppingList = new List<string>();

            Console.WriteLine("===== 购物清单管理 =====\n");

            while (true)
            {
                Console.WriteLine("1. 添加商品");
                Console.WriteLine("2. 删除商品");
                Console.WriteLine("3. 查看清单");
                Console.WriteLine("4. 退出");
                Console.Write("请选择操作:");

                string choice = Console.ReadLine();

                switch (choice)
                {
                    case "1":
                        Console.Write("请输入要添加的商品:");
                        string item = Console.ReadLine();
                        shoppingList.Add(item);
                        Console.WriteLine($"已添加:{item}\n");
                        break;

                    case "2":
                        if (shoppingList.Count == 0)
                        {
                            Console.WriteLine("清单为空!\n");
                            break;
                        }
                        Console.WriteLine("当前清单:");
                        for (int i = 0; i < shoppingList.Count; i++)
                        {
                            Console.WriteLine($"{i + 1}. {shoppingList[i]}");
                        }
                        Console.Write("请输入要删除的序号:");
                        int index = int.Parse(Console.ReadLine()) - 1;
                        if (index >= 0 && index < shoppingList.Count)
                        {
                            string removed = shoppingList[index];
                            shoppingList.RemoveAt(index);
                            Console.WriteLine($"已删除:{removed}\n");
                        }
                        else
                        {
                            Console.WriteLine("序号无效!\n");
                        }
                        break;

                    case "3":
                        Console.WriteLine("===== 购物清单 =====");
                        if (shoppingList.Count == 0)
                        {
                            Console.WriteLine("清单为空!");
                        }
                        else
                        {
                            for (int i = 0; i < shoppingList.Count; i++)
                            {
                                Console.WriteLine($"{i + 1}. {shoppingList[i]}");
                            }
                        }
                        Console.WriteLine($"总计:{shoppingList.Count} 件商品\n");
                        break;

                    case "4":
                        Console.WriteLine("再见!");
                        return;

                    default:
                        Console.WriteLine("无效的选择!\n");
                        break;
                }
            }
        }
    }
}

运行示例:

===== 购物清单管理 =====

1. 添加商品
2. 删除商品
3. 查看清单
4. 退出
请选择操作:1
请输入要添加的商品:牛奶
已添加:牛奶

1. 添加商品
2. 删除商品
3. 查看清单
4. 退出
请选择操作:1
请输入要添加的商品:面包
已添加:面包

1. 添加商品
2. 删除商品
3. 查看清单
4. 退出
请选择操作:3
===== 购物清单 =====
1. 牛奶
2. 面包
总计:2 件商品

1. 添加商品
2. 删除商品
3. 查看清单
4. 退出
请选择操作:4
再见!

本章总结

  • ✅ 理解了数组的概念和使用
  • ✅ 学会了声明、初始化和访问数组
  • ✅ 掌握了数组的遍历和常见操作
  • ✅ 了解了 List 动态数组
  • ✅ 实践了购物清单管理系统