zoukankan      html  css  js  c++  java
  • 浅谈C#数组(一)

      如果需要使用同一类型的多个对象,可以使用数组和集合(后面介绍)。C#用特殊的记号声明,初始化和使用数组。Array类在后台发挥作用,它为数组中的元素排序和过滤提供了多个方法。使用枚举器,可以迭代数组中的所有元素。
      如果需要使用不同类型的多个对象,可以使用Tuple(元组)类型。


    一.简单数组(一维数组)
      数组是一种数据结构,它可以包含同一个类型的多个元素。

      1.数组的声明
      在声明数组时,先定义数组中的元素类型,其后是一对空方括号和一个变量名。
      int[] myArray;

      2.数组的初始化
      声明了数组之后,就必须为数组分配内存,以保存数组的所有元素。数组是引用类型,所以必须给它分配堆上的内存。为此,应使用new运算符,指定数组中元素的类型和数量来初始化数组的变量。
      myArray = new int[4];
      在声明和初始化数组后,变量myArray就引用了4个整数值,它们位于托管堆上:
      
      在指定了数组的大小后,就不能重新设置数组的大小。如果事先不知道数组中应包含多少个元素,就可以使用集合。
      除了在两个语句中声明和初始化数组之外,还可以在一个语句中声明和初始化数组:
      int[] myArray = new int[4];
      还可以使用数组初始化器为数组的每个元素复制。数组初始化器只能在声明数组变量时使用,不能在声明数组之后使用。
      int[] myArray = new int[4]{1,3,5,7};
      如果用花括号初始化数组,可以不指定数组的大小,因为编译器会自动统计元素的个数:
      int[] myArray = new int[]{1,3,5,7};
      也可以使用更简单的形式:
      int[] myArray = {1,3,5,7};

      3.访问数组元素
      在声明和初始化数组之后,就可以使用索引器访问其中的元素了。数组只支持有整型参数的索引器。
      索引器总是以0开头,表示第一个元素。可以传递给索引器的最大值是元素个数减1,因为索引从0开始:
      int[] myArray = {1,3,5,7};
      int v1 = myArray[0];
      int v2 = myArray[1];
      myArray[3] = 4;
      可以使用数组的Length属性获取元素的个数。

      4.数组中使用引用类型
      数组除了能声明预定义类型的数组,还可以声明自定义类型的数组。
      public class Person
      {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public override string ToString()
        {
          return String.Format("{0} {1}", FirstName, LastName);
        }
      }

      Person[] myPersons = new Person[2];
      myPersons[0] = new Person { FirstName = "Ayrton", LastName = "Senna" };
      myPersons[1] = new Person { FirstName = "Michael", LastName = "Schumacher" };

      如果数组中的元素是引用类型,就必须为每个数组元素分配内存。如果使用了数组中未分配内存的元素,就会抛出NullReferenceException类型的异常。
      下面是内存情况:
      
      对自定义类型也可以使用数组初始化器:
      Person[] myPersons2 =
      {
        new Person { FirstName="Ayrton", LastName="Senna"},
        new Person { FirstName="Michael", LastName="Schumacher"}
      };

    二.多维数组
      多维数组用两个或多个整数来索引。
      在C#中声明多维数组,需要在方括号中加上逗号。数组在初始化时应指定每一维的大小(也称为阶)。
      int[,] twoDim = new int[3,3];
      twoDim[0,0] = 1;
      twoDim[0,1] = 2;
      twoDim[0,2] = 3;
      twoDim[1,0] = 4;
      twoDim[1,1] = 5;
      twoDim[1,2] = 6;
      twoDim[2,0] = 7;
      twoDim[2,1] = 8;
      twoDim[2,2] = 9;
      声明数组之后,就不能修改其阶数了。
      也可以使用初始化器来初始化多维数组:
      int[,] twoDim ={
        {1,2,3},
        {4,5,6},
        {7,8,9}
        };
      使用数组初始化器时,必须初始化数组的每个元素,不能遗漏任何元素。
      声明一个三位数组:
      int[,,] threeDim ={
        {{1,2},{3,4}},
        {{5,6},{7,8}},
        {{9,10},{11,12}}
        };
      Console.WriteLine(threeDim[0,1,1]);

    三.锯齿数组
      二维数组的大小对应于一个矩形,而锯齿数组的大小设置比较灵活,在锯齿数组中,每一行都可以有不同的大小。
      在声明锯齿数组时,要依次放置左右括号。在初始化锯齿数组时,只在第一对方括号中设置该数组包含的行数。定义各行中元素个数的第二个方括号设置为空,因为这类数组的每一行包含不同的元素个数。之后,为每一行指定行中的元素个数:
      int[][] jagged = new int[3][];
      jagged[0] = new int[2]{1,2};
      jagged[1] = new int[4]{3,4,5,6};
      jagged[2] = new int[3]{7,8};
      迭代锯齿数组中的所有元素的代码可以放在嵌套的for循环中。在外层的for循环中迭代每一行,在内层的for循环中迭代一行中的每个元素:
      for(int row = 0;row<jagged.Length;row++)
      {
        for(int element = 0;element<jagged[row].Length;element++)
        {
          Console.WriteLine("row:{0}, element:{1},value:{2}",row,element,jagged[row][element]);
        }
      }

    四.Array类
      用方括号声明数组是C#中使用Array类的表示法。在后台使用C#语法,会创建一个派生自抽象基类Array的新类。这样,就可以使用Array类为每个C#数组定义的方法和属性了。
      Array类实现的其它属性有LongLength和Rank。如果数组包含的元素个数超出了整数的取值范围,就可以使用LongLength属性来获得元素个数。使用Rank属性可以获得数组的维数。

      1.创建数组
      Array类是一个抽象类,所以不能使用构造函数来创建数组。但除了使用C#语法创建数组实例之外,还可以使用静态方法CreateInstance()创建数组。如果事先不知道元素的类型,该静态方法就很有用,因为类型可以作为Type对象传递给CreateInstance()方法。
      CreateInstance()方法的第一个参数是元素的类型,第二个参数定义数组的大小。
      可以使用SetValue()方法设置对应元素的值,用GetValue()方法读取对应元素的值。
      Array intArray1 = Array.CreateInstance(typeof(int), 5);
      for (int i = 0; i < 5; i++)
      {
        intArray1.SetValue(33, i);
      }

      for (int i = 0; i < 5; i++)
      {
        Console.WriteLine(intArray1.GetValue(i));
      }
      还可以将已经创建的数组强制转换称声明为int[]的数组:
      int[] intArray2 = (int[])intArray1;
      CreateInstance()方法有许多重载版本,可以创建多维数组和索引不基于0的数组。
      //创建一个2X3的二维数组,第一维基于1,第二维基于10:
      int[] lengths = { 2, 3 };
      int[] lowerBounds = { 1, 10 };
      Array racers = Array.CreateInstance(typeof(Person), lengths, lowerBounds);

      racers.SetValue(new Person { FirstName = "Alain", LastName = "Prost" }, index1: 1, index2: 10);
      racers.SetValue(new Person
      {
        FirstName = "Emerson",
        LastName = "Fittipaldi"
      }, 1, 11);
      racers.SetValue(new Person { FirstName = "Ayrton", LastName = "Senna" }, 1, 12);
      racers.SetValue(new Person { FirstName = "Michael", LastName = "Schumacher" }, 2, 10);
      racers.SetValue(new Person { FirstName = "Fernando", LastName = "Alonso" }, 2, 11);
      racers.SetValue(new Person { FirstName = "Jenson", LastName = "Button" }, 2, 12);

      Person[,] racers2 = (Person[,])racers;
      Person first = racers2[1, 10];
      Person last = racers2[2, 12];

      2.复制数组
      因为数组是引用类型,所以将一个数组变量赋予另一个数组变量,就会得到两个引用同一数组的变量。
      数组实现ICloneable接口。这个接口定义的Clone()方法会复制数组,创建数组的浅表副本。

      如果数组的元素是值类型,Clone()方法会复制所有值:
      int[] a1 = {1,2};
      int[] a2 = (int[])a1.Clone();
      如果数组包含引用类型,只复制引用。

      除了使用Clone()方法之外,还可以使用Array.Copy()方法创建浅表副本。
      Person[] beatles = {
        new Person { FirstName="John", LastName="Lennon" },
        new Person { FirstName="Paul", LastName="McCartney" }
      };

      Person[] beatlesClone = (Person[])beatles.Clone();
      Person[] beatlesClone2 = new Person[2];
      Array.Copy(beatlesClone,beatlesClone2,2);//注意与Clone的语法区别,Copy需要传递阶数相同的已有数组。(还可以使用CopyTo()方法)

      3.排序
      Array类使用快速排序算法对数组中的元素进行排序。Sort()方法需要数组中的元素实现IComparable接口。因为简单类型(如String,Int32)实现IComparable接口,所以可以对包含这些类型的元素排序。
        string[] names = {
        "Christina Aguilera",
        "Shakira",
        "Beyonce",
        "Gwen Stefani"
        };

        Array.Sort(names);

        foreach (string name in names)
        {
          Console.WriteLine(name);
        }

      如果对数组使用使用自定义类,就必须实现IComparable接口。这个接口只定义了一个方法CompareTo()方法,如果要比较的对象相等,该方法就返回0.如果该实例应排在参数对象的前面,该方法就返回小于i0de值。如果该实例应排在参数对象的后面,该方法就返回大于0的值。
      public class Person : IComparable<Person>
      {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public override string ToString()
        {
          return String.Format("{0} {1}",
          FirstName, LastName);
        }

        public int CompareTo(Person other)
        {
          if (other == null) throw new ArgumentNullException("other");

          int result = this.LastName.CompareTo(other.LastName);
          if (result == 0)
          {
            result = this.FirstName.CompareTo(other.FirstName);
          }

          return result;
        }

      }

      客户端代码:
      Person[] persons = {
      new Person { FirstName="Damon", LastName="Hill" },
      new Person { FirstName="Niki", LastName="Lauda" },
      new Person { FirstName="Ayrton", LastName="Senna" },
      new Person { FirstName="Graham", LastName="Hill" }
      };
      Array.Sort(persons);
      foreach (Person p in persons)
      {
        Console.WriteLine(p);
      }

      如果Person对象的排序方式与上述不同,或者不能修改在数组中用作元素的类,就可以实现IComparer接口或IComparer<T>接口。这两个接口定义了方法Compare()方法。机型比较的类必须实现这两个接口之一。

      public enum PersonCompareType
      {
        FirstName,
        LastName
      }
      //通过使用实现了IComparer<T> 泛型接口的PersonComparer类比较Person对象数组。
      public class PersonComparer : IComparer<Person>
      {
        private PersonCompareType compareType;

        public PersonComparer(PersonCompareType compareType)
        {
          this.compareType = compareType;
        }


        #region IComparer<Person> Members

        public int Compare(Person x, Person y)
        {
            if (x == null) throw new ArgumentNullException("x");
            if (y == null) throw new ArgumentNullException("y");

          switch (compareType)
          {
            case PersonCompareType.FirstName:
              return x.FirstName.CompareTo(y.FirstName);
            case PersonCompareType.LastName:
              return x.LastName.CompareTo(y.LastName);
            default:
              throw new ArgumentException(
              "unexpected compare type");
          }
        }

        #endregion
      }
      客户端代码:
      Person[] persons = {
      new Person { FirstName="Damon", LastName="Hill" },
      new Person { FirstName="Niki", LastName="Lauda" },
      new Person { FirstName="Ayrton", LastName="Senna" },
      new Person { FirstName="Graham", LastName="Hill" }
      };
      Array.Sort(persons,
      new PersonComparer(PersonCompareType.FirstName));

      foreach (Person p in persons)
      {
        Console.WriteLine(p);
      }

    五.数组作为参数
      数组可以作为参数传递给方法,也可以从方法中返回。

      1.数组协变
      数组支持协变。这表示数组可以声明为基类,其派生类型的元素可以赋值于数组元素。
      static void DisPlay(object[] o)
      {
        //..
      }
      可以给该方法传递一个Person[]。
      数组协变只能用于引用类型,不能用于值类型。

      2.ArraySegment<T>
      结构ArraySegment<T>表示数组的一段。如果需要使用不同的方法处理某个大型数组的不同部分,那么可以把相应的数组部分复制到各个方法。
      ArraySegment<T>结构包含了关于数组段的信息(偏移量和元素个数)。
      static void Main()
      {
        int[] ar1 = { 1, 4, 5, 11, 13, 18 };
        int[] ar2 = { 3, 4, 5, 18, 21, 27, 33 };
        var segments = new ArraySegment<int>[2]
        {
          new ArraySegment<int>(ar1, 0, 3),
          new ArraySegment<int>(ar2, 3, 3)
        };


        var sum = SumOfSegments(segments);
        Console.WriteLine("sum of all segments: {0}", sum);

      }

      static int SumOfSegments(ArraySegment<int>[] segments)
      {
      int sum = 0;
      foreach (var segment in segments)
      {
        for (int i = segment.Offset; i < segment.Offset + segment.Count; i++)
        {
            sum += segment.Array[i];
        }

      }
      return sum;
      }

      数组段不复制原数组的元素,但原数组可以通过ArraySegment<T>访问。如果数组段中的元素改变了,这些变化就会反映到原数组中。

     

  • 相关阅读:
    Scala并发编程react、loop代码实战具体解释
    Linux内核通知链模块
    STL栈的应用之表达式求值
    ansi 控制码表及颜色代码
    关于substring的char[]共享
    jQuery几个经典表单应用整理回想
    ShareSDK for Android 2.3.10已经公布
    cocos2dx坐标系
    一种Android数据请求框架
    css 小问题解决方法整理
  • 原文地址:https://www.cnblogs.com/afei-24/p/6738128.html
Copyright © 2011-2022 走看看