- Generic - Phương thức
- Lớp List
- Khởi tạo List
- Thêm, bớt, chèn và đọc các phần tử
- Tìm kiếm trong List
- Sắp xếp phần tử trong danh sách
Collection trong C#
Một collection (bộ, tập hợp) là một nhóm các đối tượng có sự liên quan đến nhau.
Số đối tượng trong collect có thể thay đổi tăng giảm. Có nhiều loại collection,
chúng được tập hợp vào namespace System.Collections
.
Thường thì một lớp collection có các phương thức để thêm, bớt, lấy tổng phần tử.
.NET cung cấp một số các lớp collection kiểu Generic như:
List<T>
,
Dictionary<TKey, TValue>
,
Stack<T>
... những lớp generic này ở namespace
System.Collections.Generic
Tìm hiểu phần này bạn cần nắm vững về Generic trong C# trước
Ngoài ra namespace
System.Collections cũng có các lớp
collection mà không sử dụng generic như: ArrayList
,
Stack
, Queue
...
Các giao diện - interface về collect mà bạn có thể sử dụng
Interface | Mô tả |
IEnumerable<T> | Triển khai nó nếu muốn duyệt phần tử bằng foreach ,
nó định nghĩa phương thức GetEnumerator trả về một enumerator. |
ICollection<T> |
Giao diện này được triển khai bở các generic collection.
Với nó lấy tổng phần tử bằng thuộc tính Count ,
copy các phần tử vào mảng bằng CopyTo , thêm bớt
phần tử với Add , Remove ,
Clear
|
IList<T> |
Giao diện này kế thừa ICollection<T> là một danh sách
các phần tử truy cập được
theo vị trí của nó. Nó có indexer, phương thức để chèn phần tử
xóa phần tử Insert RemoveAt .
|
ISet<T> | Giao diện triển khai bởi các tập hợp |
IDictionary<TKey,TValue> | Giao diện để triển khai loại dữ liệu lưu trữ theo cặp key, value. |
ILookup<TKey,TValue> | Giao diện để triển khai loại dữ liệu lưu trữ theo cặp key, value. Nhưng cho phép một key có nhiều giá trị |
IComparer<TKey,TValue> | Giao diện để triển khai cho phép so sánh để sắp xếp Collection |
IEqualityComparer<TKey,TValue> | Giao diện để triển khai cho phép so sánh bằng |
Lớp List<T>
Lớp collection List
là lớp triển khai các giao diện
IList, ICollection, IEnumerable nó quản lý danh sách các đối tượng cùng
kiểu. Bạn có thể thêm, bớt, truy cập, sắp xếp các phần tử trong danh sách bằng các phương thức nó cung cấp
như Add
, AddRange
, Insert
, RemoveAt
, Remove
...
các phương thức này chi tiết theo ví dụ dưới
Khởi tạo một danh sách List, mà các phần tử có kiểu element_type:
var list = new List<element_type>();
Ví dụ, xây dựng danh sách các sản phẩm, sản phẩm có kiểu Product
tự định nghĩa như sau - lớp sản phẩm hỗ trợ so sánh
với sản phẩm khác nên triển khai
IComparable,
cho phép hiện lấy một chuỗi thông tin bằng ToString với định dạng nào đó
nên triển khai giao diện
IFormattable
Mã nguồn xây dựng lớp Product
trong file Product.cs như sau:
using System; namespace CS017_Generic { public class Product : IComparable<Product>, IFormattable { public int ID {set; get;} public string Name {set; get;} // tên public double Price {set; get;} // giá public string Origin {set; get;} // xuất xứ public Product(int id, string name, double price, string origin) { ID = id; Name = name; Price = price; Origin = origin; } //Triển khai IComparable, cho biết vị trí sắp xếp so với đối tượng khác // trả về 0 - cùng vị trí; trả về > 0 đứng sau other; < 0 đứng trước trong danh sách public int CompareTo(Product other) { // sắp xếp về giá double delta = this.Price - other.Price; if (delta > 0) // giá lớn hơn xếp trước return -1; else if (delta < 0) // xếp sau, giá nhỏ hơn return 1; return 0; } // Triển khai IFormattable, lấy chuỗi thông tin của đối tượng theo định dạng // format hỗ trợ "O" và "N" public string ToString(string format, IFormatProvider formatProvider) { if (format == null) format = "O"; switch (format.ToUpper()) { case "O": // Xuất xứ trước return $"Xuất xứ: {Origin} - Tên: {Name} - Giá: {Price} - ID: {ID}"; case "N": // Tên xứ trước return $"Tên: {Name} - Xuất xứ: {Origin} - Giá: {Price} - ID: {ID}"; default: // Quăng lỗi nếu format sai throw new FormatException("Không hỗ trợ format này"); } } // Nạp chồng ToString override public string ToString() => $"{Name} - {Price}"; // Quá tải thêm ToString - lấy chỗi thông tin sản phẩm theo định dạng public string ToString(string format) => this.ToString(format, null); } }
Khởi tạo List<T>
Để khởi tạo một danh sách rỗng, dùng toán tử new
var numbers = new List<int>(); // danh sách số nguyên var products = new List<Product>(); // danh sách Product
Khởi tạo danh sách có sẵn một số phần tử, thì các phần tử
liệt kê sau {}
var numbers = new List<int>() {1,2,3,4}; // khởi tạo 4 phần tử var products = new List<Product>() // khởi tạo 1 phần tử { new Product(1, "Iphone 6", 100, "Trung Quốc") };
Thêm, xóa, chèn và đọc phần tử trong List<T>
1 THÊM PHẦN TỬ vào cuối danh sách sử dụng phương thức Add
var p = new Product(2, "IPhone 7", 200, "Trung Quốc"); products.Add(p); // Thêm p vào cuối List products.Add(new Product(3, "IPhone 8", 400, "Trung Quốc")); // thêm đối tượng mới vào cuối List
Nếu muốn thêm nhiều phần tử một lúc (mảng các phần tử), dùng AddRange
var arrayProducts = new Product[] // Mảng 2 phần tử { new Product(4, "Glaxy 7", 500, "Việt Nam"), new Product(5, "Glaxy 8", 700, "Việt Nam"), }; products.AddRange(arrayProducts); // Nối các phần tử của mảng vào danh sách
2 CHÈN PHẦN TỬ vào danh sách, phần tử sẽ ở vị trí chỉ ra dùng phương thức
Insert(index, object)
hoặc chèn cả một mảng InsertRange(index,, arrayObject)
.
Trong đó index
là vị trí chèn phần tử (0 là đầu tiên).
products.Insert(3, new Product(6, "Macbook Pro", 1000, "Mỹ")); // chèn phần tử vào vị trí index 3, (thứ 4)
3 ĐỌC PHẦN TỬ trong List bạn dùng indexer với chỉ số (chỉ số bắt đầu từ 0). Ví dụ lấy phần tử ở Index = 1;
var pro = products[2]; // đọc phần tử có index = 2 Console.WriteLine(pro.ToString());
Để duyệt qua các phần tử bạn có thể dùng lệnh for
hoặc foreach
// Duyệt qua tất cả các phần tử bằng for // products.Count = lấy tổng phần tử trong List for (int i = 1; i < products.Count; i++) { var pi = products[i - 1]; Console.WriteLine(pi.ToString()); } // Duyệt qua các phần tử bằng foreach foreach (var pi in products) { Console.WriteLine(pi.ToString()); }
4 XÓA PHẦN TỬ trong List - để xóa phần tử ở vị trí index dùng RemoveAt(index)
, để xóa cả một đoạn count phần tử, từ vị trí index
dùng RemoveRange(index, count);
,
để xóa toàn bộ (làm rỗng) gọi Clear();
hoặc RemoveAll();
products.RemoveAt(0); // xóa phần tử đầu tien products.RemoveRange(products.Count - 2, 2); // xóa 2 phần tử cuối
Khi bạn có tham chiếu đến đối tượng đang có trong List, cũng có thể loại
nó bằng Remove(obj);
var pro_rm = products[1]; products.Remove(pro_rm); // xóa phần tử pro_rm
Tìm kiếm thông tin trong List
Một số phương thức cho phép tìm kiếm, tra cứu vị trị trí các phần tử trong List
Phương thức | Mô tả |
---|---|
IndexOf(obj) |
Tìm index của đối tượng trong List |
LastIndexOf(obj) |
Tìm index của phần tử cuối cùng có giá trị bằng obj trong List |
FindIndex |
Tìm kiếm trả về Index |
FindLastIndex |
Tìm kiếm trả về Index cuối |
Find(Predicate) |
Tìm kiếm trả về phần tử |
FindAll(Predicate) |
Tìm kiếm trả về danh sách phần tử |
FindLast |
Tìm kiếm trả về phần tử cuối tìm thấy |
Trong các phương thức trên, có các phương thức ví dụ Find,
chứa tham số là delegate bool Predicate<in T>(T obj);
,
nó là hàm callback, trả về true là phần tử phù hợp trả về
(xem về sử dụng delegate trong C#)
Ví dụ sau đây là một Delegate phù hợp gán cho tham số Predicate
// Delegate trả về true khi tên bằng "Glaxy 8" (Product ob) => { return (ob.Name == "Glaxy 8"); }
Đoạn mã này có thể làm tham số cho Find, FindAll ...
Product foundpr1 = products.Find( (Product ob) => { return (ob.Name == "Glaxy 8");} ); if (foundpr1 != null) Console.WriteLine("(found) " + foundpr1.ToString("O")); // (found) Xuất xứ: Việt Nam - Tên: Glaxy 8 - Giá: 700 - ID: 5
Các delegate cũng có thể viết gọn lại
// tìm index của đối tượng có xuất xứ là "Trung Quốc" var ifound = products.FindIndex(x => x.Origin == "Trung Quốc"); // tìm các sản phẩm có giá trên 100 List<Product> p_100 = products.FindAll(product => product.Price > 100);
Nếu muốn tùy biến cao hơn Delegate, để tìm kiếm theo tham số tùy chọn, bạn có thể để Delegate trên vào lớp chức năng, ví dụ xây dựng lớp SearchNameProduct
public class SearchNameProduct { string namesearch; public SearchNameProduct(string name) { namesearch = name; } // Hàm gán cho delegage public bool search(Product p) { return p.Name == namesearch; } }
Thực hiện tìm kiếm, ví dụ
Product pr1 = products.Find( (new SearchNameProduct("Glaxy 8")).search); // Tìm sản phẩm có tên Glaxy 8 Product pr2 = products.Find( (new SearchNameProduct("IPhone 6")).search); // Tìm sản phẩm có tên IPhone 6
Sắp xếp các phần tử trong List
Để sắp xếp các phần tử trong danh sách, nếu phần tử đó có triển
khai giao diện IComparable thì chỉ việc gọi Sort()
để có danh sách theo thứ tự.
Ví dụ trên, lớp Product có triển khai IComparable, với phương thức CompareTo, thì sản phẩm nào có giá cao hơn xếp trước, có giá thấp hơn xếp sau.
products.Sort(); foreach (var pi in products) { Console.WriteLine(pi.ToString("N")); }
Kết quả Tên: Macbook Pro - Xuất xứ: Mỹ - Giá: 1000 - ID: 6 Tên: Glaxy 8 - Xuất xứ: Việt Nam - Giá: 700 - ID: 5 Tên: Glaxy 7 - Xuất xứ: Việt Nam - Giá: 500 - ID: 4 Tên: IPhone 8 - Xuất xứ: Trung Quốc - Giá: 400 - ID: 3 Tên: IPhone 7 - Xuất xứ: Trung Quốc - Giá: 200 - ID: 2 Tên: Iphone 6 - Xuất xứ: Trung Quốc - Giá: 100 - ID: 1
Bạn cũng có thể tùy biến cách thức sắp xếp bằng cách cung cấp hàm callback dạng deleage hai tham số kiểu cùng với kiểu phần tử cho Search, thay vì sắp xếp mặc định như trên.
Nhớ là trả về > 0 thì phần tử hiện tại xếp sau phần tử tham số.
Ví dụ hàm callback sau xếp ID nhỏ lên trước
products.Sort( (p1, p2) => { if (p1.ID > p2.ID) return 1; else if (p1.ID == p2.ID) return 0; return -1; } ); foreach (var pi in products) { Console.WriteLine(pi.ToString("N")); }
Một số phương thức khác tham khảo
Contains(obj)
kiểm tra có chứa phần tử objReverse()
đảo thứ tự danh sáchToArray()
copy các phần tử ra mảng
Tham khảo mã nguồn ví dụ: CS017_GenericCollect (Method) - Git hoặc tải về excs017-3 (List)