GIÁO TRÌNH

Giáo trình ngôn ngữ lập trình C#

Science and Technology

Danh sách mảng

Tác giả: Khuyet Danh

Việc sử dụng mảng có kích thước cố định là không thích hợp cũng như là chúng ta không thể đoán trước được kích thước của mảng cần thiết.

Lớp ArrayList là một kiểu dữ liệu mảng mà kích thước của nó được gia tăng một cách động theo yêu cầu. ArrayList cung cấp một số phương thức và những thuộc tính cho những thao tác liên quan đến mảng. Một vài phương thức và thuộc tính quan trọng của ArrayList được liệt kê trong bảng 9.3 như sau:

Các phương thức và thuộc tính của ArrayList
Phương thức- thuộc tính Mục đích
Adapter() Phương thức static tạo một wrapper ArrayList cho đốitượng IList
FixedSize() Phương thức static nạp chồng trả về sanh sách đối tượng như là một wrapper. Danh sách có kích thước cố định, các thành phần của nó có thể được sửa chữa nhưng không thểthêm hay xóa.
ReadOnly() Phương thức static nạp chồng trả về danh sách lớp như là một wrapper, chỉ cho phép đọc.
Repeat() Phương thức static trả về một ArrayList mà những thànhphần của nó được sao chép với giá trị xác định.
Synchronized() Phương thức static trả về danh sách wrapper được thread-safe
Capacity Thuộc tính để get hay set số thành phần trong ArrayList.
Count Thuộc tính nhận số thành phần hiện thời trong mảng
IsFixedSize Thuộc tính kiểm tra xem kích thước của ArrayList có cốđịnh hay không
IsReadOnly Thuộc tính kiểm tra xem ArrayList có thuộc tính chỉ đọc hay không.
IsSynchronized Thuộc tính kiểm tra xem ArrayList có thread-safe haykhông
Item() Thiết lập hay truy cập thành phần trong mảng tại vị trí xácđịnh. Đây là bộ chỉ mục cho lớp ArrayList.
SyncRoot Thuộc tính trả về đối tượng có thể được sử dụng để đồngbộ truy cập đến ArrayList
Add() Phương thức public để thêm một đối tượng vào ArrayList
AddRange() Phương thức public để thêm nhiều thành phần của mộtICollection vào cuối của ArrayList
BinarySearch() Phương thức nạp chồng public sử dụng tìm kiếm nhị phận để định vị một thành phần xác định trong ArrayList đượcsắp xếp.
Clear() Xóa tất cả các thành phần từ ArrayList
Clone() Tạo một bản copy
Contains() Kiểm tra một thành phần xem có chứa trong mảng haykhông
CopyTo() Phương thức public nạp chồng để sao chép một ArrayListđến một mảng một chiều.
GetEnumerator() Phương thức public nạp chồng trả về một enumerator dùngđể lặp qua mảng
GetRange() Sao chép một dãy các thành phần đến một ArrayList mới
IndexOf() Phương thức public nạp chồng trả về chỉ mục vị trí đầu tiênxuất hiện giá trị
Insert() Chèn một thành phần vào trong ArrayList
InsertRange(0 Chèn một dãy tập hợp vào trong ArrayList
LastIndexOf() Phương thức public nạp chồng trả về chỉ mục trị trí cuối cùng xuất hiện giá trị.
Remove() Xóa sự xuất hiện đầu tiên của một đối tượng xác định.
RemoveAt() Xóa một thành phần ở vị trí xác định.
RemoveRange() Xóa một dãy các thành phần.
Reverse() Đảo thứ tự các thành phần trong mảng.
SetRange() Sao chép những thành phần của tập hợp qua dãy nhữngthành phần trong ArrayList.
Sort() Sắp xếp ArrayList.
ToArray() Sao chép những thành phần của ArrayList đến một mảngmới.
TrimToSize() Thiết lập kích thước thật sự chứa các thành phần trongArrayList

Khi tạo đối tượng ArrayList, không cần thiết phải định nghĩa số đối tượng mà nó sẽ chứa. Chúng ta thêm vào ArrayList bằng cách dùng phương thức Add(), và danh sách sẽ quan lý những đối tượng bên trong mà nó lưu giữ. Ví dụ sau minh họa sử dụng ArrayList.

Sử dụng ArrayList.

-----------------------------------------------------------------------------

namespace Programming_CSharp

{

using System;

using System.Collections;

// một lớp đơn giản để lưu trữ trong mảng

public class Employee

{

public Employee(int empID)

{

this.empID = empID;

}

public override string ToString()

{

return empID.ToString();

}

public int EmpID

{

get

{

return empID;

{

set

}

empID = value;

}

}

private int empID;

}

public class Tester

{

static void Main()

{

ArrayList empArray = new ArrayList();

ArrayList intArray = new ArrayList();

// đưa vào mảng

for( int i = 0; i < 5; i++)

{

empArray.Add( new Employee(i+100));

intArray.Add( i*5 );

}

// in tất cả nội dung

for(int i = 0; i < intArray.Count; i++)

{

Console.Write("{0}",intArray[i].ToString());

}

Console.WriteLine("\n");

// in tất cả nội dung của mảng

for(int i = 0; i < empArray.Count; i++)

{

Console.Write("{0} ",empArray[i].ToString());

}

Console.WriteLine("\n");

Console.WriteLine("empArray.Count: {0}", empArray.Count); Console.WriteLine("empArray.Capacity: {0}", empArray.Capacity);

}

}

}

-----------------------------------------------------------------------------

Kết quả:

0 5 10 15 20

100 101 102 103 104

empArray.Count: 5

empArray.Capacity: 16

-----------------------------------------------------------------------------

Với lớp Array phải định nghĩa số đối tượng mà mảng sẽ lưu giữ. Nếu cố thêm các thành phần vào trong mảng vượt quá kích thước mảng thì lớp mảng sẽ phát sinh ra ngoại lệ. Với ArrayList thì không cần phải khai báo số đối tượng mà nó lưu giữ. ArrayList có một thuộc tính là Capacity, đưa ra số thành phần mà ArrayList có thể lưu trữ:

public int Capacity {virtual get; virtual set;}

Mặc định giá trị của Capacity là 16, nếu khi thêm thành phần thứ 17 vào thì Capacity tự động nhân đôi lên là 32. Nếu chúng ta thay đổi vòng lặp như sau:

for( int i = 0; i < 17; i++)

thì kết quả giống như sau:

0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

empArray.Capacity: 32

Chúng ta có thể làm bằng tay để thay đổi giá trị của Capacity bằng hay lớn hơn giá trị Count. Nếu thiết lập giá trị của Capacity nhỏ hơn giá trị của Count, thì chương trình sẽ phát sinh ra ngoại lệ có kiểu như sau ArgumentOutOfRangeException.

Thực thi IComparable

Giống như tất cả những tập hợp, ArrayList cũng thực thi phương thức Sort() để cho phép chúng ta thực hiện việc sắp xếp bất cứ đối tượng nào thực thi IComparable. Trong ví dụ kế tiếp sao, chúng ta sẽ bổ sung đối tượng Employee để thực thi IComparable:

public class Employee: IComparable

Để thực thi giao diện IComparable, đối tượng Employee phải cung cấp một phương thức

CompareTo(): public int CompareTo(Object o) { Employee r = (Employee) o; return this.empID.CompareTo(r.empID); }

Phương thức CompareTo() lấy một đối tượng làm tham số, đối tượng Employee phải so sánh chính nó với đối tượng này và trả về -1 nếu nó nhỏ hơn đối tượng này, 1 nếu nó lớn hơn, và cuối cùng là giá trị 0 nếu cả hai đối tượng bằng nhau. Việc xác định thứ tự của Employee thông qua thứ tự của empID là một số nguyên. Do vậy việc so sánh sẽ được ủy quyền cho thành viên empID, đây là số nguyên và nó sẽ sử dụng phương thức so sánh mặc định của kiểu dữ liệu nguyên. Điều này tương đương với việc so sánh hai số nguyên. Lúc này chúng ta có thể thực hiện việc so sánh hai đối tượng Employee. Để thấy được cách sắp xếp, chúng ta cần thiết phải thêm vào các số nguyên vào trong mảng Employee, các số nguyên này được lấy một cách ngẫu nhiên. Để tạo một giá trị ngẫu nhiên, chúng ta cần thiết lập một đối tượng của lớp Random, lớp này sẽ trả về một số giả số ngẫu nhiên. Phương thức Next() được nạp chồng, trong đó một phiên bản cho phép chúng ta truyền vào một số nguyên thể hiện một số ngẫu nhiên lớn nhất mong muốn. Trong trường hợp này chúng ta đưa vào số 10 để tạo ra những số ngẫu nhiên từ 0 đến 10:

Random r = new Random();

r.Next(10);

Ví dụ minh họa sau tạo ra một mảng các số nguyên và một mảng Employee, sau đó đưa vào những số ngẫu nhiên, rồi in kết quả. Sau đó sắp xếp cả hai mảng và in kết quả cuối cùng.

Sắp xếp mảng số nguyên và mảng Employee.

-----------------------------------------------------------------------------

namespace Programming_CSharp

{

using System;

using System.Collections;

// một lớp đơn giản để lưu trữ trong mảng

public class Employee : IComparable

{

public Employee(int empID)

{

this.empID = empID;

}

public override string ToString()

{

return empID.ToString();

}

public int EmpID

{

get

{

return empID;

}

set

{

empID = value;

}

}

// So sánh được delegate cho Employee

// Employee sử dụng phương thức so sánh

// mặc định của số nguyên

public int CompareTo(Object o)

{

Employee r = (Employee) o;

return this.empID.CompareTo(r.empID);

}

private int empID;

}

public class Tester

{

static void Main()

{

ArrayList empArray = new ArrayList();

ArrayList intArray = new ArrayList();

Random r = new Random();

// đưa vào mảng

for( int i = 0; i < 5; i++)

{

empArray.Add( new Employee(r.Next(10)+100));

intArray.Add( r.Next(10) );

}

// in tất cả nội dung

for(int i = 0; i < intArray.Count; i++)

{

Console.Write("{0}",intArray[i].ToString());

}

Console.WriteLine("\n");

// in tất cả nội dung của mảng

for(int i = 0; i < empArray.Count; i++)

{

Console.Write("{0} ",empArray[i].ToString());

}

Console.WriteLine("\n");

// sắp xếp và hiển thị mảng nguyên

intArray.Sort();

for(int i = 0; i < intArray.Count; i++)

{

Console.Write("{0} ", intArray[i].ToString());

}

Console.WriteLine("\n");

// sắp xếp lại mảng

Employee empArray.Sort();

// hiển thị tất cả nội dung của mảng

Employee for(int i = 0; i < empArray.Count; i++)

{

Console.Write("{0}", empArray[i].ToString());

}

Console.WriteLine("\n");

}

}

}

-----------------------------------------------------------------------------

Kết quả:

8 5 7 3 3

105 103 107 104 102

3 3 5 7 8

102 103 104 105 107

-----------------------------------------------------------------------------

Kết quả chỉ ra rằng mảng số nguyên và mảng Employee được tạo ra với những số ngẫu nhiên, và sau đó chúng được sắp xếp và được hiển thị lại giá trị mới theo thứ tự sau khi sắp xếp.

Thực thi IComparer

Khi chúng ta gọi phương thức Sort() trong ArrayList thì phương thức mặc định của IComparer được gọi, nó sử dụng phương pháp QuickSort để gọi thực thi IComparable phương thức CompareTo() trong mỗi thành phần của ArrayList.

Chúng ta có thể tự do tạo một thực thi của IComparer riêng, điều này cho phép ta có thể tùy chọn cách thực hiện việc sắp xếp các thành phần trong mảng. Trong ví dụ minh họa tiếp sau đây, chúng ta sẽ thêm trường thứ hai vào trong Employee là yearsOfSvc. Và Employee có thể được sắp xếp theo hai loại là empID hoặc là yearsOfSvc.

Để thực hiện được điều này, chúng ta cần thiết phải tạo lại sự thực thi của IComparer để truyền cho phương thức Sort() của mảng ArrayList. Lớp IComparer EmployeeComparer biết về những đối tượng Employee và cũng biết cách sắp xếp chúng. EmployeeComparer có một thuộc tính, WhichComparision có kiểu là Employee.EmployeeComparer.ComparisionType:

public Employee.EmployeeComparer.ComparisionType WhichComparision

{

get

{

return whichComparision;

}

set

{

wichComparision = value;

}

}

ComparisionType là kiểu liệt kê với hai giá trị, empID hay yearsOfSvc, hai giá trị này chỉ ra rằng chúng ta muốn sắp xếp theo ID hay số năm phục vụ:

public enum ComparisionType

{

EmpID, Yrs

};

Trước khi gọi Sort(), chúng ta sẽ tạo thể hiện của EmployeeComparer và thiết lập giá trị cho thuộc tính kiểu ComparisionType:

Employee.EmployeeComparer c = Employee.GetComparer();

c.WhichComparision = Employee.EmployeeComparer.ComparisionType.EmpID;

empArray.Sort(c);

Khi chúng ta gọi Sort() thì ArrayList sẽ gọi phương thức Compare() trong Employee- Comparer, đến lượt nó sẽ ủy quyền việc so sánh cho phương thức Employee.CompareTo(),và truyền vào thuộc tính WhichComparision của nó:

Compare(object lhs, object rhs) { Employee l = (Employee) lhs; Employee r = (Employee) rhs; return l.CompareTo(r.WhichComparision); }

Đối tượng Employee phải thực thi một phiên bản riêng của CompareTo() để thực hiện việc so sánh:

public int CompareTo(Employee rhs, Employee.EmployeeComparer.ComparisionType which) { switch (which) { case Employee.EmployeeComparer.ComparisionType.EmpID: return this.empID.CompareTo( rhs.empID); case Employee.EmployeeComparer.ComparisionType.Yrs: return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc); } return 0; }

Sau đây là ví dụ sau thể hiện đầy đủ việc thực thi IComparer để cho phép thực hiện sắp xếp theo hai tiêu chuẩn khác nhau. Trong ví dụ này mảng số nguyên được xóa đi để làm cho đơn giản hóa ví dụ.

Sắp xếp mảng theo tiêu chuẩn ID và năm công tác.

-----------------------------------------------------------------------------

namespace Programming_CSharp

{

using System;

using System.Collections;

//lớp đơn giản để lưu trữ trong mảng

public class Employee : IComparable

{

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();

}

// phương thức tĩnh để nhận đối tượng

Comparer public static EmployeeComparer GetComparer()

{

return new Employee.EmployeeComparer();

}

public int CompareTo(Object rhs)

{

Employee r = (Employee) rhs;

return this.empID.CompareTo(r.empID);

}

// thực thi đặc biệt được gọi bởi custom comparer

public int CompareTo(Employee rhs,

Employee.EmployeeComparer.ComparisionType which)

{

switch (which)

{

case Employee.EmployeeComparer.ComparisionType.EmpID:

return this.empID.CompareTo( rhs.empID);

case Employee.EmployeeComparer.ComparisionType.Yrs:

return this.yearsOfSvc.CompareTo( rhs.yearsOfSvc);

}

return 0;

}

// lớp bên trong thực thi IComparer

public class EmployeeComparer : IComparer

{

// định nghĩa kiểu liệt kê

public enum ComparisionType

{

EmpID, Yrs

};

// yêu cầu những đối tượng Employee tự so sánh với nhau

public int Compare( object lhs, object rhs)

{

Employee l = (Employee) lhs;

Employee r = (Employee) rhs;

return l.CompareTo(r, WhichComparision);

}

public Employee.EmployeeComparer.ComparisionType WhichComparision

{

get

{

return whichComparision;

}

set

{

whichComparision = value;

}

}

private Employee.EmployeeComparer.ComparisionType whichComparision;

}

private int empID;

private int yearsOfSvc = 1;

}

public class Teser

{

static void Main()

{

ArrayList empArray = new ArrayList();

Random r = new Random();

// đưa vào mảng

for(int i=0; i < 5; i++)

{

empArray.Add( new Employee(r.Next(10)+100, r.Next(20)));

}

// hiển thị tất cả nội dung của mảng

Employee for(int i=0; i < empArray.Count; i++)

{

Console.Write("\n{0} ", empArray[i].ToString());

}

Console.WriteLine("\n");

// sắp xếp và hiển thị mảng

Employee.EmployeeComparer c = Employee.GetComparer();

c.WhichComparision = Employee.EmployeeComparer.ComparisionType.EmpID;

empArray.Sort(c);

// hiển thị nội dung của mảng

for(int i=0; i < empArray.Count; i++)

{

Console.Write("\n{0} ", empArray[i].ToString());

}

Console.WriteLine("\n");

c.WhichComparision = Employee.EmployeeComparer.ComparisionType.Yrs;

empArray.Sort(c);

// hiển thị nội dung của mảng

for(int i=0; i < empArray.Count; i++)

{

Console.Write("\n{0} ", empArray[i].ToString());

}

Console.WriteLine("\n");

}

}

}

-----------------------------------------------------------------------------

Kết quả:

ID: 100. Years of Svc: 16

ID: 102. Years of Svc: 8

ID: 107. Years of Svc: 17

ID: 105. Years of Svc: 0

ID: 101. Years of Svc: 3

ID: 100. Years of Svc: 16

ID: 101. Years of Svc: 3

ID: 102. Years of Svc: 8

ID: 105. Years of Svc: 0

ID: 107. Years of Svc: 17

ID: 105. Years of Svc: 0

ID: 101. Years of Svc: 3

ID: 102. Years of Svc: 8

ID: 100. Years of Svc: 16

ID: 107. Years of Svc: 17

-----------------------------------------------------------------------------

Khối đầu tiên hiển thị kết quả thứ tự vừa nhập vào. Trong đó giá trị của empID, và yearsOfSvc được phát sinh ngẫu nhiên. Khối thứ hai hiển thị kết quả sau khi sắp theo empID,và khối cuối cùng thể hiện kết quả được xếp theo năm phục vụ.