9.6.1 實現IComparable接口
像所有集合類一樣,List實現了Sort()方法,它允許您對所有實現了IComparable接口的對象進行排序。在下一個例子中,您將修改Employee類以實現IComparable:
public class Employee : IComparable<Employee>
實現IComparable<Employee>接口,Employee對象必須提供CompareTo()方法:
public int CompareTo(Employee rhs)
{
return this.empID.CompareTo(rhs.empID);
}
CompareTo()方法把Employee做為參數。我們知道使用Employee是因為這是一個類型安全的集合。當前Employee對象必須把它自己跟做為參數傳遞進來的Employee進行對比,如果返回-1,表示它小於參數,如果大於參數則返回1,如果兩者相等則返回0。這意味著決定大於、小於、等於的是Employee。在本例中,您委托成員empId進行比較。empId成員是一個int類型,並使用了整數類型默認的CompareTo()方法,以對比兩個值之間的大小。
System.Int32類實現了Icomparable<Int32>接口,所以您可以把比較的職責委托給整數類型。
您現在准備對員工數組列表(empList)進行排序,為了查看排序是否正常工作,您需要隨機地添加整數和Employee實例到它們各自的數組中。創建隨機數,需要實例化Random類;調用Random對象的Next()方法產生隨機數。Next()方法是一個重載方法;一種版本允許您傳遞一個整數值,表示您想要的最大隨機數。在本例中,您將傳遞10來產生一個0到10之間的隨機數:(譯者注:參數為10,最大的隨機數只能為9)
Random r = new Random();
r.Next(10);
例9-14創建了一個整型數組和一個Employee數組,並給兩者填充隨機數,並打印它們的值。隨後排序數組並打印新值。
例9-14 排序整數和employee數組
using System;
using System.Collections.Generic;
using System.Text;
namespace IComparable
{
// 一個簡單的用於存儲在數組中的類
public class Employee : IComparable<Employee>
{
private int empID;
public Employee( int empID )
{
this.empID = empID;
}
public override string ToString( )
{
return empID.ToString( );
}
public bool Equals( Employee other )
{
if ( this.empID == other.empID )
{
return true;
}
else
{
return false;
}
}
// Employee使用整數默認的CompareTo方法
public int CompareTo( Employee rhs )
{
return this.empID.CompareTo( rhs.empID );
}
}
public class Tester
{
static void Main( )
{
List<Employee> empArray = new List<Employee>( );
List<Int32> intArray = new List<Int32>( );
// 產生整數和employee的ID的隨機數
Random r = new Random( );
// 填充數組
for ( int i = 0; i < 5; i++ )
{
// 添加隨機的employee的id
empArray.Add( new Employee( r.Next( 10 ) + 100 ) );
// 添加隨機的整數
intArray.Add( r.Next( 10 ) );
}
// 顯示整型數組中的所有內容
for ( int i = 0; i < intArray.Count; i++ )
{
Console.Write( "{0} ", intArray[i].ToString( ) );
}
Console.WriteLine( "\n" );
// 顯示Employee數組中的所有內容
for ( int i = 0; i < empArray.Count; i++ )
{
Console.Write( "{0} ", empArray[i].ToString( ) );
}
Console.WriteLine( "\n" );
// 整型數組排序
intArray.Sort( );
for ( int i = 0; i < intArray.Count; i++ )
{
Console.Write( "{0} ", intArray[i].ToString( ) );
}
Console.WriteLine( "\n" );
// employee數組排序並顯示
//原文的下面這兩句應該注釋掉,現在還沒用到
//Employee.EmployeeComparer c = Employee.GetComparer( );
//empArray.Sort(c);
empArray.Sort( );
// 顯示Employee數組中的所有內容
for ( int i = 0; i < empArray.Count; i++ )
{
Console.Write( "{0} ", empArray[i].ToString( ) );
}
Console.WriteLine( "\n" );
}
}
}
輸出結果:
4 5 6 5 7
108 100 101 103 103
4 5 5 6 7
100 101 103 103 108
輸出顯示整型數組和Employee數組產生的是隨機數。排序後,顯示的是已經進行排序後的值。
9.6.2 實現IComparer接口
當您在List中調用Sort(),默認的IComparer(譯者注:這裡可能是錯誤,應該為IComparable)實現被調用,它調用IComparable所實現的CompareTo()方法對List內的每個元素進行快速排序。
當您想控制排列方式時,可以自由地創建自己的IComparer實現。在下一個例子中,將在Employee中添加第二個字段:yearsOfSvc。您希望在List中按兩種字段empID或yearsOfSvc來對Employee對象進行排序。
為了達到這個目的,需要創建IComparer的實現,用於傳遞給List中Sort()方法的參數。這個IComparer類是EmployeeComparer,它讓Employee對象知道如何進行排序。
EmployeeComparer類有一個WhichComparison屬性,它是Employee. EmployeeComparer.ComparisonType類型:
public Employee.EmployeeComparer.ComparisonType WhichComparison
{
get{return whichComparison;}
set{whichComparison = value;}
}
ComparisonType是一個枚舉類型,它有兩個值:empID或yearsOfSvc(指示您希望按員工ID還是工齡進行排序):
public enum ComparisonType
{
EmpID,
YearsOfService
};
在調用Sort()方法之前,創建EmployeeComparer的實例並設置它的ComparisonType屬性:
Employee.EmployeeComparer c = Employee.GetComparer();
c.WhichComparison=Employee.EmployeeComparer.ComparisonType.EmpID;
empArray.Sort(c);
調用Sort()方法之時,List會調用EmployeeComparer中的Compare方法,並把當前要對比的字段通過WhickComparison屬性傳遞給Employee.CompareTo()方法:
public int Compare( Employee lhs, Employee rhs )
{
return lhs.CompareTo( rhs, WhichComparison );
}
Employee對象必須實現一個自定義的CompareTo()方法,用於獲得比較方式並按照要求進行對比:
public int CompareTo(Employee rhs,
Employee.EmployeeComparer.ComparisonType which)
{
switch (which)
{
case Employee.EmployeeComparer.ComparisonType.EmpID:
return this.empID.CompareTo(rhs.empID);
case Employee.EmployeeComparer.ComparisonType.Yrs:
return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc);
}
return 0;
}
例9-15是這個例子的完整代碼。為了簡化例子,整型數組已經被移除,用於輸出的Employee的ToString()方法也增加了代碼以讓您看得到排序的效果。
例9-15 按員工的ID和工齡進行排序
using System;
using System.Collections.Generic;
using System.Text;
namespace IComparer
{
public class Employee : IComparable<Employee>
{
private int empID;
private int yearsOfSvc = 1;
public Employee(int empID)
{
this.empID = empID;
}
public Employee(int empID, int yearsOfSvc)
{
this.empID = empID;
this.yearsOfSvc = yearsOfSvc;
}
public override string ToString()
{
return "ID:" + empID.ToString() +
". Years of Svc:" + yearsOfSvc.ToString();
}
public bool Equals(Employee other)
{
if (this.empID == other.empID)
{
return true;
}
else
{
return false;
}
}
//靜態方法,用於獲取一個Comparer對象
public static EmployeeComparer GetComparer()
{
return new Employee.EmployeeComparer();
}
public int CompareTo(Employee rhs)
{
return this.empID.CompareTo(rhs.empID);
}
//通過自定義comparer來調用指定的實現
public int CompareTo(Employee rhs,
Employee.EmployeeComparer.ComparisonType which)
{
switch (which)
{
case Employee.EmployeeComparer.ComparisonType.EmpID:
return this.empID.CompareTo(rhs.empID);
case Employee.EmployeeComparer.ComparisonType.Yrs:
return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc);
}
return 0;
}
//實現IComparer接口的嵌套類
public class EmployeeComparer : IComparer<Employee>
{
private Employee.EmployeeComparer.ComparisonType
whichComparison;
//比較方式枚舉
public enum ComparisonType
{
EmpID,
Yrs
};
public bool Equals(Employee lhs, Employee rhs)
{
return this.Compare(lhs, rhs) == 0;
}
public int GetHashCode(Employee e)
{
return e.GetHashCode();
}
public int Compare(Employee lhs, Employee rhs)
{
return lhs.CompareTo(rhs, WhichComparison);
}
public Employee.EmployeeComparer.ComparisonType
WhichComparison
{
get { return whichComparison; }
set { whichComparison = value; }
}
}
}
public class Tester
{
static void Main()
{
List<Employee> empArray = new List<Employee>();
Random r = new Random();
for (int i = 0; i < 5; i++)
{
//添加一個隨機的員工ID
empArray.Add(new Employee(
r.Next(10) + 100, r.Next(20)));
}
//顯示Employee數組的所有內容
for (int i = 0; i < empArray.Count; i++)
{
Console.Write("\n{0} ", empArray[i].ToString());
}
Console.WriteLine("\n");
//排序並顯示Employee數組
Employee.EmployeeComparer c = Employee.GetComparer();
c.WhichComparison =
Employee.EmployeeComparer.ComparisonType.EmpID;
empArray.Sort(c);
//顯示Employee數組的所有內容
for (int i = 0; i < empArray.Count; i++)
{
Console.Write("\n{0} ", empArray[i].ToString());
}
Console.WriteLine("\n");
c.WhichComparison = Employee.EmployeeComparer.ComparisonType.Yrs;
empArray.Sort(c);
for (int i = 0; i < empArray.Count; i++)
{
Console.Write("\n{0} ", empArray[i].ToString());
}
Console.WriteLine("\n");
}
}
}
輸出結果:
ID: 103. Years of Svc: 11
ID: 108. Years of Svc: 15
ID: 107. Years of Svc: 14
ID: 108. Years of Svc: 5
ID: 102. Years of Svc: 0
ID: 102. Years of Svc: 0
ID: 103. Years of Svc: 11
ID: 107. Years of Svc: 14
ID: 108. Years of Svc: 15
ID: 108. Years of Svc: 5
ID: 102. Years of Svc: 0
ID: 108. Years of Svc: 5
ID: 103. Years of Svc: 11
ID: 107. Years of Svc: 14
ID: 108. Years of Svc: 15
第一塊輸出顯示的是Employee對象被加進List時的情形。員工ID值和工齡是隨機順序的。第二塊顯示的是按員工ID排序後的結果,第三塊顯示的是按工齡排序後的結果。
如果您如例9-11那樣創建自己的集合,並希望實現IComparer,可能需要確保所有放在列表中的類型都實現了IComparer接口(這樣他們才有可能被排序),這可以通過前面講述的約束來實現。