System.Array.Sort 有很多对集合的操作,比如排序,查找,克隆等等,你可以利用这个类加深对 IComparer、IComparable、ICloneable 以及泛型委托、匿名方法、Lambda 表达式的理解。
自定义后面用到的 Person 类和 PersonComparer 类。
自定义 Person 类,该类有两个属性:last name 和 first name,重构了 ToString 方法。Person 类还继承 IComparable 和 ICloneable 接口,实现了 CompareTo 和 Clone 方法。CompareTo 方法在后面的集合排序时会用到,它通常是默认的排序。如下所示:
monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px">public class Person : IComparable, ICloneable
{
public Person()
{
}
public Person(string firstname, string lastname)
{
this.FirstName = firstname;
this.LastName = lastname;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return string.Format("{0} {1}", FirstName, LastName);
}
public int CompareTo(object obj)
{
Person other = obj as Person;
return this.LastName.CompareTo(other.LastName);
}
public object Clone()
{
return new Person(FirstName, LastName);
}
}
自定义 PersonComparer 类,该类内定义了比较类型,可以选择按 last name 比较,还是 first name。PersonComparer 类同时继承 IComparer 接口的非泛型形式和泛型形式,实现了它们相应的 Compare 方法,该方法在后面的集合排序中会用到。如下所示:
public class PersonComparer : IComparer, IComparer<Person>
{
public enum PersonCompareType
{
FirstName,
LastName
}
private PersonCompareType compareType;
public PersonComparer(PersonCompareType compareType)
{
this.compareType = compareType;
}
public int Compare(object x, object y)
{
Person p1 = x as Person;
Person p2 = y as Person;
switch (compareType)
{
case PersonCompareType.FirstName:
return p1.FirstName.CompareTo(p2.FirstName);
case PersonCompareType.LastName:
return p1.LastName.CompareTo(p2.LastName);
default:
throw new ArgumentException("unexpected compare type");
}
}
public int Compare(Person x, Person y)
{
Person p1 = x;
Person p2 = y;
switch (compareType)
{
case PersonCompareType.FirstName:
return p1.FirstName.CompareTo(p2.FirstName);
case PersonCompareType.LastName:
return p1.LastName.CompareTo(p2.LastName);
default:
throw new ArgumentException("unexpected compare type");
}
}
}
Array.Sort 有 17 个“重载”,如下所示:
Sort(Array)
Sort(Array, Array)
Sort(Array, IComparer)
Sort(Array, Array, IComparer)
Sort(Array, Int32, Int32)
Sort(Array, Array, Int32, Int32)
Sort(Array, Int32, Int32, IComparer)
Sort(Array, Array, Int32, Int32, IComparer)
Sort<T>(T[])
Sort<T>(T[], IComparer<T>)
Sort<T>(T[], Comparison<T>)
Sort<T>(T[], Int32, Int32)
Sort<T>(T[], Int32, Int32, IComparer<T>)
Sort<TKey, TValue>(TKey[], TValue[])
Sort<TKey, TValue>(TKey[], TValue[], IComparer<TKey>)
Sort<TKey, TValue>(TKey[], TValue[], Int32, Int32)
Sort<TKey, TValue>(TKey[], TValue[], Int32, Int32, IComparer<TKey>)
从它们的参数,你可以先想象一下:
- 这些方法可以分成非泛型形式和泛型形式,也就是每个非泛型形式基本都会对应一个泛型形式;
- 如果我们要对一个集合排序,肯定是要比较的,并且默认是升序;
- 如果集合是字符串,那很好办,几乎不需要做什么;
- 可如果集合是对象,那我们就必须指定两个对象之间如何比较以及比较什么;
- 如果有两个集合,一个集合是当键,另一个集合是当值,用键为值排序,这当然也是可以的,比如 Sort<TKey, TValue>;
- 针对不同的情况,可以指定不同的比较方法,比如,参数中带 ICompare 的。
如果集合仅仅是字符串数组,那很好办,如下代码所示,按升序排序:
string[] names = { "Emerson Fittipaldi", "Niki Lauda", "Ayrton Senna", "Michael Schumacher" };
Array.Sort(names);
但外国人的姓在后面,要是按我们的规矩如何按姓排序?字符串数组就不行了,此时就需要自定义的 Person 类,假设有如下 Person 数组:
Person[] persons = {
new Person("Emerson", "Fittipaldi"),
new Person("Niki", "Lauda"),
new Person("Ayrton", "Senna"),
new Person("Michael", "Schumacher")
};
按姓的升序排序,采用 Sort(Array) 这个方法,如下代码所示:
Array.Sort(persons);
因为前面定义的 Person 类是继承了 IComparer 接口,并实现了 CompareTo 方法,里面规定了按姓 last name 比较,所以 Array.Sort 方法就知道如何排序了。
如果有时需要按 first name 排序,有时需要用 last name,该怎么办?这是就需要自定义的类 PersonComparer。采用 Sort(Array, IComparer) 这个方法,代码如下所示:
Array.Sort(persons, new PersonComparer(PersonComparer.PersonCompareType.FirstName));
Console.WriteLine("Sort By First Name:");
foreach (Person p in persons)
{
Console.WriteLine(" " + p);
}
Array.Sort(persons, new PersonComparer(PersonComparer.PersonCompareType.LastName));
Console.WriteLine("Sort By Last Name:");
foreach (Person p in persons)
{
Console.WriteLine(" " + p);
}
结果:
Sort By First Name:
Ayrton Senna
Emerson Fittipaldi
Michael Schumacher
Niki Lauda
Sort By Last Name:
Emerson Fittipaldi
Niki Lauda
Michael Schumacher
Ayrton Senna
如果我们事先没定义 PersonComparer 类,也没问题,可以采用 Sort<T>(T[], Comparison<T>) 这个方法,参数 Comparison<T> 是泛型委托,这意味着我们可以用匿名方法或 Lambda 表达式来规定 Person 如何比较以及比较什么,代码如下所示:
Array.Sort(persons,
delegate(Person p1, Person p2)
{
return p1.FirstName.CompareTo(p2.FirstName);
});
Array.Sort(persons,
delegate(Person p1, Person p2)
{
return p1.LastName.CompareTo(p2.LastName);
});
或
Array.Sort(persons, (p1, p2) => p1.FirstName.CompareTo(p2.FirstName));
Array.Sort(persons, (p1, p2) => p1.LastName.CompareTo(p2.LastName));
如果用两个集合呢?一个集合用当键,另一个集合当值,用键为值排序,如下代码所示:
Person[] personKeys = {
new Person("Emerson", "Fittipaldi"),
new Person("Niki", "Lauda"),
new Person("Ayrton", "Senna"),
new Person("Michael", "Schumacher")
};
Person[] personValues = {
new Person("Emerson", "Fittipaldi"),
new Person("Niki", "Lauda"),
new Person("Ayrton", "Senna"),
new Person("Michael", "Schumacher")
};
Array.Sort<Person, Person>(personKeys, personValues);
Array.Sort<Person, Person>(personKeys, personValues, new PersonComparer(PersonComparer.PersonCompareType.FirstName));
Sort 两个参数的泛型方法,personValues 会按 personKeys 的 LastName 排序;而 Sort 三个参数的泛型方法,指定了 personKeys 按 FirstName 排序。
知道 Array.Sort 的用法,其他方法,像检索方法,BinarySearch、Find*** 开头,以及 indexOf 相关,就很好理解了。
下载 Demo