数组

数组

  • 数组的定义
  • Array、ArrayList、List的区别
  • 数组的方法

数组的定义

数组是允许将多个数据项作为集合来处理的机制。CLR支持一维、多维和交错数组(即数组构成的数组)。所有数组类型都隐式地从System.Array抽象类派生,后者又派生自System.Object。这意味着数组始终是引用类型。

Array、ArrayList、List的区别

所有数组都隐式实现IEnumerable,ICollection,IList。

Array

数组在C#中最早出现的。在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单。但是数组存在一些不足的地方。在数组的两个数据间插入数据是很麻烦的,而且在声明数组的时候必须指定数组的长度,数组的长度过长,会造成内存浪费,过段会造成数据溢出的错误。如果在声明数组时我们不清楚数组的长度,就会变得很麻烦。

string[] s = new string[2] { "12","22"};
Array ss = new string[2] { "12", "22" };

在方法中声明局部变量来引用初始化好的数组时,可利用c#的“隐式类型的局部变量”功能来简化代码

string[] name = new[] { "jack", "tom"};     //隐式数组
var name = new[] { "jack", "tom", null };   //隐式类型的局部变量
var name = new[] { "jack", "tom", 123 };    //会报错,找不到隐式类型数组的最佳类型
string[] name = { "jack", "tom", null };    //初始化数组的额外语法糖
var name = new[] { "jack", "tom"};          //会报错,无法使用数组初始值设定项初始化隐式类型化的变量

上式中第三个报错原因是因为StringInt32的共同基类是Object,意味着编译器不得不创建Object引用的一个数组然后对123进行装箱,并让最后一个数组元素引用已装箱的、值为123的一个Int32
针对数组的这些缺点,C#中最先提供了ArrayList对象来克服这些缺点。

ArrayList

ArrayList是命名空间System.Collections下的一部分,在使用该类时必须进行引用,同时继承了IList接口,提供了数据存储和检索。ArrayList对象的大小是按照其中存储的数据来动态扩充与收缩的。所以,在声明ArrayList对象时并不需要指定它的长度。

ArrayList ss = new ArrayList();
ss.Add(1);
ss.Add("qwe");   //是被允许的

我们从上面的例子看,在List中,我们不仅插入了字符串qwe,而且插入了数字1。这样在ArrayList中插入不同类型的数据是允许的。因为ArrayList会把所有插入其中的数据当作为Object类型来处理,在我们使用ArrayList处理数据时,很可能会报类型不匹配的错误,也就是ArrayList不是类型安全的。在存储或检索值类型时通常发生装箱和取消装箱操作,带来很大的性能耗损。

因为ArrayList存在不安全类型与装箱拆箱的缺点,所以出现了泛型的概念。

泛型List

List类是ArrayList类的泛型等效类,它的大部分用法都与ArrayList相似。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。

List<string> qwe = new List<string>();  //必须声明类型
qwe.Add(1);                             //会报错,无法从"int"转换为"string"

数组的方法

多维数组

int[,] array1 = new int[,] { { 1, 2, 3 }, { -1, -2, -3 } };//定义一个2*3的二维int数组,并初始化 
int[][] array2 = new int[][]    //定义交错数组并初始化
{
    new int[] { 1, 3, 5, 7, 9 },  
    new int[] { 0, 2, 4, 6 },  
    new int[] { 11, 22 }
};
for (int i = 0; i < array2.Length; i++)     //for循环遍历第一维
    foreach (int j in array2[i])            //foreach循环遍历第二维
        //foreach循环下,在C#3.0、C#4.0中输出五个9四个6两个22,c#5.0之后的版本与预期一样
        Console.WriteLine(j);

数组排序

//定义数组 
int[] arrSid = { 5, 3, 4, 2, 1 };
string[] arrSname = { "张三", "李四", "王五", "麻子", "淘气" };
Array.Sort(arrSid);                     //对数组排序,升序
foreach (int j in arrSid)
    Console.WriteLine(j);               //输出12345
Array.Sort(arrSid);                     //对数组排序
Array.Reverse(arrSid);                  //翻转数组实现降序
foreach (int j in arrSid)
    Console.WriteLine(j);               //输出54321
//输出原始数组:原始数组:张三(5)->李四(4)->王五(3)->麻子(2)->淘气(1)-> 
Console.WriteLine("原始数组:");
for (int i = 0; i < arrSid.Length; i++)
    Console.Write("{0}({1})->", arrSname[i], arrSid[i]);
Console.WriteLine();

//根据学号关键字排序,左边是关键字,右边是对应的项
Array.Sort(arrSid, arrSname);

//并输出排序后的数组:淘气(1)->麻子(2)->王五(3)->李四(4)->张三(5)  
Console.WriteLine("排序以后数组:");
for (int i = 0; i < arrSid.Length; i++)
    Console.Write("{0}({1})->", arrSname[i], arrSid[i]);

数组复制

首先先对值类型数组进行复制

int[] ls = new int[2] { 1,2 };
int[] copy = new int[2];
//copy.Initialize();        //该方法通过调用值类型的默认构造函数,初始化值类型 System.Array 的每一个元素。
Console.WriteLine(copy[1]); //实际上没报错,虽然我没有初始化该数组。输出是0。
Array.Copy(ls, copy, 2);
copy[0] = 3;
Console.WriteLine(copy[0]); //输出3
Console.WriteLine(ls[0]);   //输出1,值类型数组通过复制得到的数组地址已经完全不一样了,因此无法改变原型。

接着对引用类型进行复制

string[] ls = new string[2] { "1","2" };
string[] copy = new string[2];
Console.WriteLine(copy[1]); //居然没报错,理论上应该是null,然后返回未将对象引用到实例的错误,暂时无法理解
Array.Copy(ls, copy, 2);
copy[0] = "3";
Console.WriteLine(copy[0]); //输出3
Console.WriteLine(ls[0]);   //输出1

引用类型数组通过复制得到的数组理论上只是地址,A改变了值,B应该也会改变值才对。实际上,这是由于string是特殊的引用类型,具体原因可以翻看我之前的博客string与StringBuilder。那么string类型看不出区别,我们看看其他引用类型

public class Student    //先定义一个student类
{
    public int num;
    public string name;
    public Student(int num,string name)
    {
        this.num = num;
        this.name = name;
    }
}
Student[] ls = new Student[2] { new Student(1, "张三"), new Student(2, "李四") };
Student[] copy = new Student[2];
Array.Copy(ls, copy, 2);
copy[0].num = 3;
copy[0].name = "王五";
Console.WriteLine(copy[0].name);//输出王五
Console.WriteLine(copy[0].num); //输出3
Console.WriteLine(ls[0].name);  //输出王五
Console.WriteLine(ls[0].num);   //输出3,可以看出Student类的复制只是浅表复制,共用一个地址。
Console.ReadKey();

Student[] copy2 = (Student[])ls.Clone();    //clone方法返回的是Object类,应该对他进行显式转换。
copy2[0].num = 4;
copy2[0].name = "赵六";
Console.WriteLine(copy[0].name);//输出赵六
Console.WriteLine(copy[0].num); //输出4
Console.WriteLine(ls[0].name);  //输出赵六
Console.WriteLine(ls[0].num);   //输出4,可以看出Copy与clone方法都是浅表复制,不同在于Copy是静态方法
//其次Clone()会创建一个新数组,Copy方法必须传递阶数相同且有足够元素的已有数组。
Console.ReadKey();

三维数组排序

List<Point3D> point3D = new List<Point3D>(){
    new Point3D(1,5,3),
    new Point3D(2,4,2),
    new Point3D(3,3,4),
    new Point3D(4,2,1),
    new Point3D(5,1,5)
};
var list1 = point3D.OrderBy(p => p.X).Tolist();
foreach(Point3D p in list1)
{
    Console.WriteLine($"X:{p.X} Y:{p.Y} Z:{p.Z}");
}
Console.WriteLine();
var list2 = point3D.OrderBy(p => p.Y).Tolist();
foreach(Point3D p in list2 )
{
    Console.WriteLine($"X:{p.X} Y:{p.Y} Z:{p.Z}");
}
Console.WriteLine();
var list3 = point3D.OrderBy(p => p.Z).Tolist();
foreach(Point3D p in list3 )
{
    Console.WriteLine($"X:{p.X} Y:{p.Y} Z:{p.Z}");
}
Console.ReadKey();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值