第5章 数组处理
数组同上一章讲的字符串一样,也是C#数据处理方面的一个基础内容。数组是一种基本的数据类型,和其他类型(比如int型、float型、double型、string型等)的区别是它能存储一组数据,而非单个数据。这个性质使得数组的应用十分广泛。
5.1 创建数组
同其他数据类型一样,数组的创建也十分容易,示例代码如下所示。
byte[] a = new byte[5];
上面的代码是建立一个名为a,长度为5的byte型一维数组。如果要访问该数组,用a[i]进行访问,数组的元素索引应该从0开始。也就是说,要访问数组的第一个元素用a[0],访问最后一个用a[4]。数组的类型可以是C#中的任意基本类型,如int型、byte型、char型、double型、float型、string型等。下面举个简单的例子。
打开VS2008,在D:\C#\ch5目录下建立一个名为arrayCreateTest的控制台应用程序。在Main()函数里添加如下代码。
int[] a1 = new int[10]; string[] a2 = new string[10]; for (int i = 0; i < 10; i++) { a1[i] = i; a2[i] = a1[i].ToString(); //转化为string型从控制台输出 Console.Write(a2[i] + " "); } Console.ReadKey();
该段代码实现的功能是先对一个整型数组赋值,然后将其转化为string型从控制台输出。运行结果如图5-1所示。
图5-1 运行结果
在上例中对数组的定义是先给其分配内存空间,然后再进行赋值。也可以采用数组的另一种初始化方式,在定义的时候同时赋值,如下所示。
int[] s = new int[5] { 1, 2, 3, 4, 5 };
下面接着介绍二维数组及多维数组,先看一个简单二维数组的定义。
double[, ] d = new double[2, 3];
二维数组的初始化和一维数组一样,除了先声明,后赋值外,也可以在声明的同时进行赋值,如下所示。
double[, ] d = new double[, ] { {2,2}, {3,3}, {4,4}, {5,5}}; //声明一个4行2 //列的二维数组并赋值
5.2 多维数组
在实际的数据处理中,三维数组就基本能处理所有问题。在此,举一个三维数组的例子并说明foreach关键字在数组处理中的应用。
打开VS2008,在D:\C#\ch5目录下建立一个名为mArrayTest的控制台应用程序。在Main()函数中添加如下代码。
int[, , ] s = new int[3, 3, 3]; for (int i = 0; i < 3; i++) //通过三个for循环对该三维数组进行赋值 { for (int j = 0; j < 3; j++) { for (int m = 0; m < 3; m++) { s[i, j, m] = i + j + m; } } } foreach (int k in s) //用foreach输出数组中的所有元素到控制台显示 { Console.Write(k.ToString()+" "); } Console.ReadKey();
运行结果如图5-2所示。
图5-2 运行结果
上例中,对声明的三维数组进行初始化,并运用foreach关键字将三维数组中的全部元素输出到控制台。介绍完一维数组和多维数组后,下面接着介绍数组中的一个灵活点——动态数组。
5.3 动态数组
在数组的使用过程中,读者有时候希望数组的长度和元素都能随着程序的运行不断改变。但改变一次就重新开辟一个新的数组对象将会十分占用内存。所以,前面所讲的数组都不能满足要求。有解决方案吗?当然有!这正是动态数组要解决的问题。.NET Framework中提供了一个ArrayList类,专门用于处理可按动态增减成员的数组。ArrayList类包含在System.Collections命名空间中,相关属性和方法如表5-1所示。
表5-1 ArrayList类的相关属性和方法
5.3.1 创建简单的动态数组
了解了ArrayList类属性和方法后,下面以一个例子说明以上的某些方法,帮助读者掌握ArrayList类的运用。
打开VS2008,在D:\C#\ch5目录下建立名为ArraylistTest的控制台应用程序,步骤如下。
(1)在命名空间中包含System.Collections,因为ArrayList类包含在该命名空间内。
(2)在Main()函数中添加如下代码。
ArrayList al = new ArrayList(); al.Add(0.05f); //向ArrayList结尾处添加4种不同类型的对象 al.Add("sss"); al.Add(' s' ); al.Add(3); Console.WriteLine("当前ArrayList里的全部元素如下:"); for (int i = 0; i < 4; i++) { Console.Write(al[i]+" "); } Console.WriteLine("\n当前ArrayList里的元素个数共有:"); Console.Write(al.Count); //获取ArrayList中的元素个数 Console.WriteLine("\n继续添加后当前ArrayList里的全部元素如下:"); al.Insert(1, "aa"); //在ArrayList索引值为1处添加字符串aa for (int i = 0; i < 5; i++) { Console.Write(al[i]+" "); } Console.WriteLine("\n倒序后当前ArrayList里的全部元素如下:"); al.Reverse(); //将ArrayList中的所有元素逆转排序 for (int i = 0; i < 5; i++) { Console.Write(al[i]+" "); } Console.WriteLine("\n删除后当前ArrayList里的全部元素如下:"); al.RemoveAt(2); for (int i = 0; i < 3; i++) { Console.Write(al[i] + " "); } Console.ReadKey();
程序运行结果如图5-3所示。
图5-3 运行结果
通过该例子,可以看出ArrayList具有以下优势。
(1)ArrayList中能够同时存储不同类型的数据,比如第一个元素是int型,第二个可以是string型,这是常规数组办不到的。
(2)ArrayList能够随时添加或删除元素,而不需开辟新的数组对象,并且代码很简单,也不需复杂的类型转化。
(3)能够随时在ArrayList的任意位置插入数据。
5.3.2 动态数组的排序
下面再看一个例子,产生一个int数组,长度为50,并向其中随机插入1~50之间的任意整数,并且不能重复,然后完成排序。对于这个例子,用ArrayList实现起来非常方便。
打开VS2008,在D:\C#\ch5目录下建立名为ArraylistTest2的控制台应用程序。打开工程,在Main()函数中添加如下代码。
int[] intArr = new int[50]; //创建数组 ArrayList myList = new ArrayList(); //创建ArrayList Random rnd = new Random(); //创建随机数类 while (myList.Count < 50) { int num = rnd.Next(1, 51); //生成1~50之间的任意整数 if (! myList.Contains(num)) myList.Add(num); //向ArrayList添加元素 } Console.WriteLine("随机插入后的数组:"); for (int i = 0; i < 50; i++) { intArr[i] = (int)myList[i]; Console.Write("{0} ", intArr[i]); } //冒泡法排序 for (int j = intArr.Length -1; j >= 0; j--) { for (int k = 0; k < j; k++) { int m = 0; if (intArr[k] < intArr[k + 1]) { m = intArr[k]; intArr[k] = intArr[k + 1]; intArr[k + 1] = m; } } } Console.WriteLine(); Console.WriteLine("排序后的数组:"); //输出排序结果 for (int i = 0; i < 50; i++) { Console.Write("{0} ", intArr[i]); } Console.ReadKey();
运行结果如图5-4所示。
图5-4 运行结果
5.4 数组的特殊操作
本节主要介绍两个字符串的特殊操作,一是将数组作为参数传递,二是数组的数组,下面将通过例子进行详细说明。
5.4.1 作为参数传递的数组
数组是一种引用类型,它能作为参数传递给函数调用。下面两节将按不同维数的数组分别进行举例介绍。
5.4.2 将一维数组作为参数传递
打开VS2008,在D:\C#\ch5目录下建立名为ArrayParaTest的控制台应用程序,在Program.cs中添加Array()函数,代码如下。
private static void Array(int[] s) { for (int i = 0; i < s.Length; i++) { Console.Write(s[i].ToString()+" "); } }
然后在Main()函数中添加如下代码。
int[] ss = new int[6] { 1, 2, 3, 4, 5, 6 }; Array(ss); Console.ReadKey();
运行结果如图5-5所示。
图5-5 运行结果
本例中,将数组s作为Array()函数的参数进行传递,在Main()函数中被赋值,将所有元素输出到控制台显示。
5.4.3 将多维数组作为参数传递
打开VS2008,在D:\C#\ch5目录下建立名为ArrayParaTest2的控制台应用程序。在Program.cs中添加mArray()函数,代码如下所示。
private static void mArray(int[, , ] ss) { foreach (int s in ss) { Console.Write(s.ToString()+" "); } }
然后在Main()函数中添加如下代码。
int[, , ] sss = new int[3, 3, 3]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { for (int m = 0; m < 3; m++) { sss[i, j, m] = i + j + m; } } } mArray(sss); Console.ReadKey();
运行结果如图5-6所示。
将多维数组作为参数传递与一维数组几乎没有什么区别。只是对多维数组元素的访问时要麻烦一些。
图5-6 运行结果
5.4.4 数组的数组
顾名思义,数组的数组意思是某数组的元素也是数组,也可以称作“复合”数组。它的特点是数组长度在数组声明的时候可以不被指定,先来看下面的一个例子。
string[][] s = new string[4][]; //定义一个二维数组 s[0] = new string[] { "a", "a", "a" }; s[1] = new string[] { "b", "b", "b", "b"}; s[2] = new string[] { "c", "c", "c", "c", "c" }; s[3] = new string[] { "d", "d", "d", "d", "d", "d" };
到这里,可能有读者会问,对这样的数组进行访问和以前的数组有区别吗?答案是肯定的。比如想输出数组s里面的所有元素,如果直接采用下面的代码。
foreach (string ss in s) { Console.Write(ss + " "); }
会出现如下错误信息。
错误1 无法将类型“string[]”转换为“string”
为什么会这样呢?因为数组s中包含的元素是string[]型,而非string型,所以正确的做法应该如下面代码所示。
foreach (string[]ss in s) { foreach (string sss in ss) { Console.Write(sss + " "); } }
先遍历访问每个子数组,再将子数组里的元素循环输出到控制台。数组的数组并不常用,但在某些特殊场合还是比较有用的,它能节省程序运行的时间。
5.5 小结
本章主要介绍了一维数组和多维数组的创建及基本的使用方法,包括数组中元素的访问和遍历输出。重点说明了动态数组类ArrayList的属性、方法和用法,以一个例子涵盖了大多数ArrayList的常见操作。最后,还介绍了作为参数传递的数组和数组的数组。总之,数组是继字符串后C#的又一个应用广泛的数据处理类型,在很多场合都能用到。