Trong C#, List<T> là một lớp tập hợp (collection) thuộc không gian tên System.Collections.Generic, được thiết kế để lưu trữ một dãy các phần tử có cùng kiểu dữ liệu T. Khác với mảng cố định, List<T> tự động mở rộng dung lượng khi thêm phần tử vượt quá giới hạn hiện tại — cơ chế này dựa trên việc cấp phát lại mảng nội bộ và sao chép dữ liệu.
Tạo danh sách kiểu generic
Cú pháp khai báo yêu cầu chỉ định rõ kiểu dữ liệu lưu trữ, đảm bảo kiểm tra kiểu tại thời điểm biên dịch:
List<string> danhSachTen = new List<string>();
List<DateTime> lichSuDangNhap = new List<DateTime>();
Các thao tác cơ bản
Thêm phần tử
Dùng phương thức Add() để chèn một phần tử vào cuối danh sách:
danhSachTen.Add("Nguyen Van A");
danhSachTen.Add("Le Thi B");
danhSachTen.Add("Tran Van C");
Lấy số lượng phần tử
Thuộc tính Count trả về số phần tử hiện tại — khác với Capacity, vốn cho biết dung lượng bộ nhớ đã cấp phát:
Console.WriteLine($"Số phần tử: {danhSachTen.Count}"); // 3
Truy cập ngẫu nhiên qua chỉ số
Hỗ trợ truy cập trực tiếp bằng toán tử ngoặc vuông [], tương tự mảng:
Console.WriteLine(danhSachTen[0]); // "Nguyen Van A"
Duyệt danh sách
Có thể sử dụng cả vòng lặp for (dựa trên chỉ số) hoặc foreach (dựa trên giá trị):
// Duyệt theo chỉ số
for (int i = 0; i < danhSachTen.Count; i++)
{
Console.WriteLine($"[{i}] {danhSachTen[i]}");
}
// Duyệt theo giá trị
foreach (string ten in danhSachTen)
{
Console.WriteLine($"Tên: {ten}");
}
Kiểm tra sự tồn tại của phần tử
Phương thức Contains() thực hiện so sánh bằng hàm Equals() mặc định của kiểu T:
if (danhSachTen.Contains("Le Thi B"))
{
Console.WriteLine("Đã tìm thấy người dùng.");
}
Trích xuất phân đoạn con
GetRange(startIndex, count) tạo một danh sách mới chứa count phần tử bắt đầu từ vị trí startIndex:
List<string> phanDoan = danhSachTen.GetRange(1, 2); // ["Le Thi B", "Tran Van C"]
Tìm vị trí đầu tiên của phần tử
IndexOf() trả về chỉ số nhỏ nhất của phần tử khớp — nếu không tìm thấy thì trả về -1:
int viTri = danhSachTen.IndexOf("Tran Van C"); // 2
Xóa phần tử
RemoveAt(index): xóa phần tử tại vị trí cụ thể.RemoveRange(startIndex, count): xóa một dãy liên tiếp.Remove(item): xóa lần xuất hiện đầu tiên của giá trị khớp.Clear(): loại bỏ toàn bộ phần tử.
// Xóa phần tử thứ hai
danhSachTen.RemoveAt(1);
// Xóa hai phần tử bắt đầu từ vị trí 0
danhSachTen.RemoveRange(0, 2);
// Xóa tất cả phần tử có giá trị "Nguyen Van A" (dùng vòng lặp ngược để tránh lỗi chỉ số)
for (int i = danhSachTen.Count - 1; i >= 0; i--)
{
if (danhSachTen[i] == "Nguyen Van A")
danhSachTen.RemoveAt(i);
}
Chuyển đổi sang mảng
Phương thức ToArray() tạo một mảng mới chứa toàn bộ phần tử hiện tại:
string[] mangTen = danhSachTen.ToArray();
Lưu ý khi xóa nhiều phần tử
Khi xóa phần tử trong vòng lặp tiến (tăng dần), chỉ số các phần tử phía sau sẽ dịch chuyển — dẫn đến khả năng bỏ sót phần tử. Giải pháp an toàn là duyệt ngược (i--) hoặc điều chỉnh thủ công biến đếm trong khối else.
Giá trị băm (Hash Code)
Mỗi đối tượng trong .NET đều có phương thức GetHashCode() để sinh ra một số nguyên đại diện cho giá trị của nó — hữu ích trong các cấu trúc như Dictionary hoặc HashSet. Tuy nhiên, GetHashCode() của một List<T> không phản ánh nội dung mà chỉ phụ thuộc vào tham chiếu đối tượng:
Console.WriteLine(42.GetHashCode()); // Giá trị băm của số nguyên
Console.WriteLine(danhSachTen.GetHashCode()); // Giá trị băm của tham chiếu List