.netframerwork中的数组

  在.netframerwork中数组是一种数据结构,它包含若干同一类型的元素。就好像我们可以把多个相同类型的若干元素放在一个容器中供我们使用。数组就好像装鸡蛋的格子,里面的元素就好像是鸡蛋。数值数组的默认值为0,引用元素的默认值为null。数组的大小是不可变的,一旦定义了大小就不可变,除非我们克隆出另外一个数组。声明一个数组是很简单的,声明数组,只需要先定义数组中元素的类型,然后后面加一个方括号

  int[] arr;

  初始化数组也是很简单的,可以有以下的几种方式。

  arr = new int[4];

  由于数组是引用类型,所以使用new关键字初始化,然后指定数组的大小。也可以把他们写在一行里

  int[] arr = new int[4];

  arr[0]=1;

  int[] arr = new int[]{2,3};

  int[] arr ={2,3};

  以上就是初始化数组,但是有一点一定要注意,初始化数组一定要指明数组的大小或者在花括号里指明数组里面的元素。而且数组的大小是不可变的。

  访问数组可以用索引访问,默认只能用整型索引。当然,也可以定制使用别的类型索引。索引从0开始,到数组的长度length-1结束。

  实例化数组了之后,我们就可以操作数组,使用数组里面的方法属性。也可以使用foreach迭代数组。至于数组为什么可以使用foreach迭代,后面会介绍。

  我们来看看数组在存储值类型和引用类型的存储结构的区别,首先数组本身就是引用类型。首先数组变量也是指向数组的指针。也就是存储的引用。下面来看看值类型的数组的存储结构图。

  

  

 myArray是变量,存储在堆栈中,指向托管堆,托管堆里的数组就直接存储int类型的值,所以int的值是存储在托管堆上的,而不是存储在栈上。 而引用类型的数组在数组中存储就不是实际的值了,而是对象的引用,看看下一幅图。

  

  其实很一目了然。这也很符合值类型跟引用类型的特征。

 

  多维数组

  多维数组有二维数组和三维数组。

  二维数组就是有两个维度,X,Y。x是一个数组,y是另外一个数组。三位就是有3个维度。

  先看一个二维数组吧

  

 int[,] arr = new int[3, 3] {
            {1,2,3},
            {4,5,6},
            {7,8,9}
            };

            Console.WriteLine(arr[2,2]);

   其实就是行列,输出9。其实可以看成数组里面的数组,这样索引就可以得到结果arr[2][2]。

  再看看三维数组吧

 int[,,] arr = new int[3,3,3] {
            {{1,2,3},{4,5,6},{7,8,9}},
            {{3,2,1},{6,5,4},{9,8,7}},
            {{2,3,1},{5,6,4},{8,9,7}}
            };

            Console.WriteLine(arr[2,2,2]);

  输出7,可以看成arr[2][2][2].就可以得到7.

  锯齿数组。以上的多维数组都是规范的,每一行拥有的列数都是对等的,但是有的数组列数是不对等的,类似

  2,3

  4,5,6

  7,8,9,10

  这就是一个不对等的数组,那么这样的数组怎么表示呢,其实也很简单

  我们首先确定行数,然后分别实例化每一行的数组就行了

            int[][] arr = new int[3][];//声明一个具有三行的数组
            arr[0] = new int[] { 2,3};
            arr[1] = new int[] { 4,5,6};
            arr[2] = new int[] { 7,8,9,10};

  这里就声明了具有3行的锯齿数组。那么我们该怎么访问呢?

  其实也不难,只需要双重迭代一下就可以了。

 int[][] arr = new int[3][];//声明一个具有三行的数组
            arr[0] = new int[] { 2,3};
            arr[1] = new int[] { 4,5,6};
            arr[2] = new int[] { 7,8,9,10};

            Console.WriteLine(arr[0][0]);

            for (int i = 0; i < arr.Length; i++)
            {
                for (int j = 0; j < arr[i].Length; j++)
                {
                    Console.WriteLine("row:{0},j:{1},value:{2}",i,j,arr[i][j]);
                }
            }

我们可以直接使用arr[0][0]来访问,但是因为我们只能确定行,不能确定列,所以容易造成数组越界的异常。

Array

  我们知道,我们声明一个int变量的时候,实际上是实例化了一个System.Int32的对象。那么这里的数组也是一样。采用标记的方法创建数组,实际上是实例化了一个继承Array抽象类的一个类。记住Array是抽象类,不能直接实例化。用C#语法创建数组实际上等价与

  Array arr = Array.CreateInstance(typeof(int), 4);

  我们使用SetValue()和GetValue()给数组添加元素和获取元素

Array arr = Array.CreateInstance(typeof(int), 4);
            arr.SetValue(1,0);
            arr.SetValue(2,1);
            arr.SetValue(3,2);
            arr.SetValue(4,3);

            Console.WriteLine(arr.GetValue(0));
            Console.WriteLine(arr.GetValue(1));
            Console.WriteLine(arr.GetValue(2));
            Console.WriteLine(arr.GetValue(3));
            Console.Read();

  当然Array有许多重载,可以创建二维,三维数组。

  数组的克隆

  数组的克隆属于浅表克隆,对于数组而言,克隆的只是数组的引用,克隆出来的数组实际上还是指向的原数组。而数组里面的元素,值类型会按值直接复制,而引用类型会复制引用。这里有一个概念,就是浅表复制和深表复制的概念,网上有很多他们之间的区别。我这里用一句话概括一下,浅表复制:值类型按值复制,引用类型复制引用。深表复制:值类型按值复制,引用类型创建对象的真正副本,指向这个对象的副本,区别就是浅表复制只会复制引用,而深表复制会直接复制对象。

  下面来看看深表复制和浅表复制的代码示例,首先看

            int[] arr1 = { 1,2,3};

            int[] arr2 = (int[])arr1.Clone();

            foreach (int i in arr2)
            {
                Console.WriteLine(i.ToString());
            }

  这里值类型会完全复制值,而引用类型却不一样,我们看一幅图

我们看看引用类型的克隆

            Person[] per1 = new Person[] {new Person(1,"Edrick","Liu"),new Person(2,"Meci","Luo") };

            Person[] per2 = (Person[])per1.Clone();

            foreach (Person p in per1)
            {
                Console.WriteLine("Name:{0},LastName:{1}",p.FirstName,p.LastName);
            }

            per2[0].FirstName = "jun";
            per2[0].LastName = "ma";

            foreach (Person p in per2)
            {
                Console.WriteLine("Name:{0},LastName:{1}", p.FirstName, p.LastName);
            }
                
            Console.Read();

运行代码我们会发现,我们对per1做的更改,per2也更改了。说明了,引用类型的的数组克隆的只是元素的引用,数组的元素实际上还是指向同一个对象。这里也有一副图

数组还有一个复制元素的方法就是CopyTo,代码如下

            Person[] per1 = new Person[] {new Person(1,"Edrick","Liu"),new Person(2,"Meci","Luo") };

            Person[] per2=new Person[2];

            per1.CopyTo(per2, 0);

            per1[0].FirstName = "jun";
            per1[0].LastName = "Ma";

            foreach (Person p in per2)
            {
                Console.WriteLine(p.FirstName+","+p.LastName);
            }

            Console.Read();

 CopyTo()跟Clone()一样都是浅表复制,但是他们有一个区别就是,Clone是创建一个新的数组,而CopyTo是吧数组元素复制到一个已经存在的,具有相同维度和足够空间的数组中去。实际上维度也只能是一维。那么我们刚刚看到的都是浅表复制,那深表复制该怎么做呢,其实就是迭代,然后利用老数组元素的数据创建新元素。

            Person[] per1 = new Person[] {new Person(1,"Edrick","Liu"),new Person(2,"Meci","Luo") };

            Person[] per2=new Person[2];

            for (int i = 0; i < per1.Length; i++)
            {
                per2[i] = new Person(per1[i].PersonId,per1[i].FirstName,per1[i].LastName);
            }

            per1[0].FirstName = "jun";
            per1[0].LastName = "ma";

            foreach (Person p in per2)
            {
                Console.WriteLine(p.FirstName+","+p.LastName);
            }

            Console.Read();

  这样就完成了深表复制,我们看到per1的改变不会影响per2.一定需要注意的就是Clone和CopyTo方法,他们是浅表复制,引用类型只是复制引用。  

  数组还有很多有用的方法和属性。至于数组为什么可以用foreach迭代。我会在别的文章里将。

 

  

posted @ 2011-11-07 22:22  刘中栋  阅读(384)  评论(0)    收藏  举报