C# - C Sharp: Collection
Tổng quan
Collection là một tập hợp các dữ liệu có liên quan đến nhau nhưng không nhất thiết phải cùng kiểu. Nó có thể được thiết lập hoặc chỉnh sửa một cách động ngay trong quá trình chương trình đang được thực thi. Việc truy cập collection nói chung khá tương tự so với việc truy cập mảng, trong đó các phần tử cũng có thể được truy cập thông qua chỉ số. Tuy vậy, cũng có một số điểm khác biệt giữa mảng và collection trong C# được thể hiện ở bảng dưới đây:
Mảng (Array) | Collection |
---|---|
Không thể thay đổi kích thước khi chương trình còn đang thực thi (run-time) | Có thể thay đổi được kích thước tại thời điểm run-time |
Các phần tử mảng có cùng kiểu dữ liệu | Các phần tử có thể khác kiểu dữ liệu |
Không có sẵn các phương thức để thao tác trên các phần tử mảng. | Có sẵn các phương thức để thao tác trên các phần tử. |
Namespace System.Collections
Đây là một namespace cho phép bạn cấu trúc và thao tác với một tập hợp các đối tượng, tập hợp này có thể bao gồm các phần tử có kiểu khác nhau. Nó cũng định nghĩa một số lượng tương đối các loại tập hợp khác nhau để người dùng lựa chọn, ví dụ như các loại mảng động, list, và dictionary.
System.Collections cũng định nghĩa các lớp và giao diện (interface) và trong chúng lại định nghĩa những tập hợp riêng, phổ biến và hay dùng nhất phải kể đến:
- Lớp ArrayList: Cung cấp một tập hợp tương tự như mảng ngoại trừ những điểm khác biệt là nó có thêm phần tử và truy xuất phần tử một cách động và nó cũng có thể chứa được tất cả các loại dữ liệu khác nhau (bao gồm cả đối tượng) cho dù những đối tượng đó có cùng kiểu hay không.
- Lớp Stack: Tập hợp này tuân theo nguyên tắc lưu trữ Last-In-First-Out (LIFO), tức là phần tử cuối cùng được chèn vào thì sẽ bị xóa đầu tiên và ngược lại.
- Lớp Hashtable: Cung cấp một tập hợp gồm các cặp khóa/giá trị (key/value) và được sắp xếp dựa trên mã băm của các khóa.
- Lớp SortedList: Cung cấp một tập hợp lưu trữ theo các cặp key/value đã được sắp xếp trước khi lưu dựa trên các key.
- IDictionary: Dùng để hiển thị tập dữ liệu theo các cặp key/value.
- IDictionaryEnumerator: Dùng để liệt kê các phần tử kiểu từ điển (dictionary).
- IEnumerable: Định nghĩa một bộ enumerator dùng để liệt kê dữ liệu trong tập hợp.
- ICollection: Chỉ định kích thước và đồng bộ hóa các phương thức cho tất cả các tập dữ liệu.
- IEnumerator: Hỗ trợ việc liệt kê các phần tử của tập dữ liệu.
- IList: Đại diện cho tập hợp các thành phần mà có thể được truy cập thông qua số chỉ mục của chúng.
Đoạn mã dưới đây minh họa việc sử dụng các lớp và giao diện thông dụng của namespace System.Collections.
using System; using System.Collections; namespace Demo { class NhanVien : DictionaryBase { public void Them(int id, string ten) { Dictionary.Add(id, ten); } public void OnRemove(int id) { Console.WriteLine("Ban se xoa ban ghi co ID: " + id); Dictionary.Remove(id); } public void ChiTiet() { IDictionaryEnumerator objIDE = Dictionary.GetEnumerator(); while (objIDE.MoveNext()) { Console.WriteLine(objIDE.Key.ToString() + "\t" + objIDE.Value); } } static void Main(string[] args) { NhanVien objNV = new NhanVien(); objNV.Them(102, "Hung"); objNV.Them(105, "Minh"); objNV.Them(106, "Quan"); Console.WriteLine("Ban dau trong Dictionary co nhung ban ghi sau:"); objNV.ChiTiet(); objNV.OnRemove(106); Console.WriteLine("Sau khi xoa, Dictionary se chua cac ban ghi sau:"); objNV.ChiTiet(); } } }
Phân tích đoạn mã: Lớp NhanVien được thừa kế từ lớp DictionaryBase, đây là một lớp trừu tượng. Thông tin chi tiết của mỗi nhân viên được chèn vào Dictionary thông qua phương thức Them() của nó. Ở đây ta xây dựng phương thức Them() trong lớp NhanVien gồm hai tham số là id và ten và hai tham số này sẽ được truyền tới phương thức Dicitonary.Add(), lớp Dictionary sẽ lưu trữ các giá trị này theo cặp key/value. Phương thức OnRemove() của DictionaryBase được ghi đè, phương thức này có một đối số để xác định key của phần tử rồi từ đó xóa cặp key/value tương ứng khỏi Dictionary thông qua phương thức Remove() của nó. Phương thức GetEnumerator() của Dictionary sẽ trả về một IDictionaryEnumerator.
Namespace System.Collections.Generic
Xét một mẫu đơn trực tuyến được sử dụng bởi các sinh viên đăng ký một bài thi của một trường đại học. Mẫu đơn có thể được sử dụng để áp dụng cho bài thi của bất kỳ khóa học nào được cung cấp bởi các trường đại học. Tương tự như vậy, trong C#, generic cho phép bạn định nghĩa các cấu trúc dữ liệu gồm các chức năng mà có thể được thực thi cho bất kỳ kiểu dữ liệu nào. Vì vậy, generic cho phép bạn tái sử dụng mã lệnh đối với các kiểu dữ liệu khác nhau mà không phải xây dựng lại mã lệnh nữa.
Để tạo generic, bạn nên sử dụng các lớp có sẵn của namespace System.Collections.Generic. Những lớp này đảm bảo sự an toàn kiểu và đây là tính năng của C# để đảm bảo một giá trị được đối xử như kiểu mà nó được khai báo.
Lưu ý: Loại namespace này tương tự như System.Collections vì cả hai đều cho phép bạn tạo các collection. Tuy nhiên, collection kiểu generic có tính năng an toàn kiểu.
System.Collections.Generic bao gồm các lớp, interface và cấu trúc cho phép định nghĩa các tập hợp chung khác nhau. Dưới đây sẽ trình bày cụ thể hơn về các thành phần này của namespace.
Lớp
Namespace System.Collections.Generic bao gồm các lớp cho phép bạn tạo các tập hợp an toàn kiểu. Bảng dưới đây liệt kê các lớp thường xuyên được sử dụng trong namespace này.
Lớp | Mô tả |
---|---|
List<T> | Cung cấp một tập hợp generic gồm các mục có thể thay đổi kích thước động |
Stack<T> | Cho phép thực hiện nguyên lý LIFO (Last In First Out), có nghĩa là mục nào được chèn vào sau cùng thì sẽ bị xóa trước tiên |
Queue<T> | Cho phép thực hiện nguyên lý FIFO, mục nào được chèn vào trước tiên thì sẽ bị xóa trước tiên |
Dictionary<K, V> | Cung cấp một tập hợp generic kiểu từ điển gồm các cặp khóa/giá trị |
SortedDictionary<K,V> | Tập hợp generic cho phép sắp xếp các cặp khóa/giá trị của các mục được sắp xếp dựa trên khóa của chúng |
LinkedList<T> | Thực hiện các danh sách nhân đôi được liên kết bằng cách lưu trữ các phần tử trong nó |
Interface và Cấu trúc
System.Collections.Generic cũng bao gồm các interface và cấu trúc mà có thể được thực thi để tạo các tập hợp an toàn kiểu. Dưới đây là một số interface và cấu trúc phổ biến:
- ICollection: Định nghĩa các phương thức để điều khiển các tập hợp generic khác nhau.
- IEnumerable: Là một interface định nghĩa một bộ liệt kê để tiến hành liệt kê một tập hợp có kiểu xác định.
- IComparer: Định nghĩa một phương thức để so sánh hai đối tượng.
- IDictionary: Biểu diễn một tập hợp chung gồm các cặp khóa và giá trị.
- IEnumerator: Hỗ trợ việc liệt kê đơn giản thông qua các phần tử của tập hợp chung.
- IList: Biểu diễn một tập hợp gồm các mục mà có thể được truy cập bằng cách sử dụng chỉ mục.
- Cấu trúc Dictionary.Enumerator: Liệt kê các phần tử của Dictionary.
- Cấu trúc Dictionary.KeyCollection.Enumerator: Liệt kê các phần tử của một Dictionary.KeyCollection.
- Cấu trúc Dictionary.ValueCollection.Enumerator: Liệt kê các phần tử của một Dictionary.ValueCollection.
- Cấu trúc Key/ValuePair: Định nghĩa một cặp khóa/giá trị.
Đoạn mã sau biểu diễn cách sử dụng các lớp, interface và cấu trúc phổ biến của namespace System.Collection.Generic.
using System; using System.Collections; using System.Collections.Generic; namespace Demo { class Student1 : IEnumerable { LinkedList<string> objList = new LinkedList<string>(); public void ChiTietSinhVien() { objList.AddFirst("An"); objList.AddFirst("Minh"); objList.AddFirst("Duong"); objList.AddFirst("Long"); objList.AddFirst("Anh"); Console.WriteLine("So luong phan tu co trong danh sach: " + objList.Count); } public void HienThi(string ten) { LinkedListNode<string> objLLN; int dem = 0; for (objLLN = objList.First; objLLN != null; objLLN = objLLN.Next) { if (objLLN.Value.Equals(ten)) { dem++; } } Console.WriteLine("Ten " + ten + " xuat hien " + dem + " lan trong danh sach"); } public IEnumerator GetEnumerator() { return objList.GetEnumerator(); } static void Main(string[] args) { Student1 objStu = new Student1(); objStu.ChiTietSinhVien(); foreach (string str in objStu) { Console.WriteLine(str); } objStu.HienThi("Anh"); } } }
Phân tích đoạn mã: Trong đoạn mã trên, lớp Student thực thi giao diện IEnumerable. Một danh sách liên kết đôi kiểu chuỗi được tạo. Phương thức ChiTietSinhVien() được định nghĩa để chèn các giá trị vào danh sách liên kết. Phương thức AddFirst() của lớp LinkedList được dùng để chèn các giá trị vào danh sách liên kết. Phương thức HienThi() có một đối số kiểu chuỗi được dùng để tìm giá trị trong danh sách. Lớp LinkedListNode có tham chiếu kiểu chuỗi được tạo trong phương thức HienThi(). Tham chiếu này được dùng để duyệt danh sách liên kết. Mỗi khi tìm thấy sự tương thích giữa giá trị của đối số phương thức HienThi() với giá trị trong danh sách thì biến dem lại tăng giá trị lên 1. Biến này dùng để đếm số lần xuất hiện của chuỗi muốn tìm. Khi phương thức GetEnumerator() được thực thi thì nó sẽ trả về một IEnumerator. IEnumerator được dùng để duyệt danh sách và hiển thị tất cả các giá trị đã lưu trong danh sách liên kết.