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#的又一个应用广泛的数据处理类型,在很多场合都能用到。