Dictionary<TKey, TValue> trong C#

Ở trong hướng dẫn trước chúng ta đã tìm hiểu về lớp List<T> trong C# - một generic collection trong loạt bài hướng dẫn về Collection trong C#. List<T> là một danh sách các phần tử được định kiểu mạnh được sử dụng rất phổ biến trong lập trình C#. Bạn có thể tìm hiểu List<T> ở link dưới đây nếu như chưa xem:

List<T> trong C# | Comdy
List<T> trong C# là gì? List<T> trong C# dùng để làm gì? Cách sử dụng List<T> trong C#.

Lớp Dictionary<TKey, TValue> trong C# giống như một cuốn từ điển tiếng Anh. Từ điển tiếng Anh là một tập hợp các từ và định nghĩa của chúng, thường được liệt kê theo thứ tự alphabet với một hoặc nhiều ngôn ngữ cụ thể. Theo cùng một cách, Dictionary trong C# là một tập hợp các khóa và giá trị, trong đó khóa giống như từ và giá trị giống như định nghĩa.

Lớp Dictionary<TKey, TValue> là một generic collection trong namespace System.Collection.Generics. Trong đó TKey biểu thị kiểu dữ liệu của khóa và TValue là kiểu dữ liệu của giá trị.

Sơ đồ sau minh họa hệ thống phân cấp lớp Dictionary<TKey, TValue>.

Dictionary<TKey, TValue> trong C#

Một đối tượng Dictionary có thể được gán cho một biến kiểu IDictionary<Tkey, TValue>hoặc Dictionary<TKey, Tvalue>.

IDictionary<int, string> dict = new Dictionary<int, string>();
        
//or

Dictionary<int, string> dict = new Dictionary<int, string>();

Trong ví dụ trên, chúng tôi đã chỉ định kiểu dữ liệu cho khóa và giá trị khi khai báo một đối tượng từ điển. Đối tượng từ điển có tên dict có kiểu dữ liệu của khóa là int và kiểu dữ liệu cho giá trị là string. Bạn có thể sử dụng bất kỳ kiểu dữ liệu C# hợp lệ nào cho các khóa và giá trị.

Trong lập trình hướng đối tượng, nên sử dụng interface thay vì sử dụng trực tiếp lớp triển khai thực hiện. Vì vậy, sử dụng interface IDictionary<TKey, TValue> để khởi tạo một đối tượng từ điển.

Lưu ý: Từ điển không cho phép các khóa trùng lặp hoặc null, nhưng giá trị có thể trùng lặp hoặc null. Các khóa phải là duy nhất nếu không nó sẽ ném ngoại lệ lúc thực thi.

Các thành viên quan trọng của Dictionary

Các thuộc tính quan trọng của Dictionary:

Thuộc tính Miêu tả
Count Trả về tổng số phần tử tồn tại trong Dictionary<TKey, TValue>.
IsReadOnly Trả về một giá trị cho biết liệu Dictionary<TKey, TValue> có ở chế độ chỉ đọc hay không.
Item Trả về hoặc thiết lập phần tử với khóa được chỉ định trong Dictionary<TKey, TValue>.
Keys Trả về danh sách các khóa của Dictionary<TKey, TValue>.
Values Trả về danh sách các giá trị trong Dictionary<TKey, TValue>.

Các phương thức quan trọng của Dictionary:

Phương thức Miêu tả
Add Thêm một phần tử vào Dictionary.
Add Thêm một cặp khóa-giá trị vào Dictionary<TKey, TValue>.
Remove Xóa phần tử xuất hiện đầu tiên được chỉ định ra khỏi Dictionary<TKey, TValue>.
Remove Xóa phần tử với khóa được chỉ định.
ContainsKey Kiểm tra xem khóa được chỉ định có tồn tại trong Dictionary<TKey, TValue> không.
ContainsValue Kiểm tra xem giá trị được chỉ định có tồn tại trong Dictionary<TKey, TValue> không.
Clear Xóa tất cả các phần tử khỏi Dictionary<TKey, TValue>.
TryGetValue Trả về true và gán giá trị với khóa được chỉ định, nếu khóa không tồn tại thì trả về false.

Thêm phần tử vào Dictionary

Sử dụng phương thức Add() để thêm cặp khóa-giá trị vào từ điển.

IDictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

IDictionary có nạp chồng phương thức Add(). Nó chấp nhận struct KeyValuePair<TKey, TValue> như là một tham số đầu vào.

Tips: Kiểm tra xem một từ điển đã lưu trữ khóa được chỉ định trước khi thêm cặp khóa-giá trị.

Ví dụ sau minh họa về thêm phần tử vào Dictionary:

IDictionary<int, string> dict = new Dictionary<int, string>();

dict.Add(new KeyValuePair<int, string>(1, "One"));
dict.Add(new KeyValuePair<int, string>(2, "Two"));

//The following is also valid
dict.Add(3, "Three");

Từ điển cũng có thể được khởi tạo bằng cú pháp khởi tạo đối tượng như dưới đây.

IDictionary<int, string> dict = new Dictionary<int, string>()
{
	{1,"One"},
	{2, "Two"},
	{3,"Three"}
};

Truy cập phần tử của Dictionary

Các phần tử của từ điển có thể được truy cập bằng nhiều cách, ví dụ: sử dụng vòng lặp foreach hoặc vòng lặp for.

Sử dụng vòng lặp foreach hoặc for để duyệt qua tất cả các phần tử của từ điển. Từ điển lưu trữ các cặp khóa-giá trị. Vì vậy, bạn có thể sử dụng kiểu KeyValuePair <TKey, TValue> hoặc một biến var trong vòng lặp foreach như dưới đây.

Dictionary<int, string> dict = new Dictionary<int, string>()
{
	{1,"One"},
	{2, "Two"},
	{3,"Three"}
};

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}

Đây là kết quả khi biên dịch và chạy chương trình:

Key: 1, Value: One
Key: 2, Value: Two
Key: 3, Value: Three

Sử dụng cho vòng lặp for để truy cập tất cả các phần tử. Sử dụng thuộc tính Count của từ điển để lấy tổng số phần tử trong từ điển.

Dictionary<int, string> dict = new Dictionary<int, string>()
{
	{1,"One"},
	{2, "Two"},
	{3,"Three"}
};


for (int i = 0; i < dict.Count; i++)
{
    Console.WriteLine("Key: {0}, Value: {1}", 
		dict.Keys.ElementAt(i), 
		dict[dict.Keys.ElementAt(i)]);
}

Đây là kết quả khi biên dịch và chạy chương trình:

Key: 1, Value: One
Key: 2, Value: Two
Key: 3, Value: Three

Từ điển có thể được sử dụng như một mảng để truy cập các phần tử riêng lẻ của nó. Chỉ định khóa (không phải chỉ mục) để lấy giá trị trong từ điển bằng cách sử dụng bộ chỉ mục như một mảng.

Dictionary<int, string> dict = new Dictionary<int, string>()
{
	{ 4, "Four" },
	{ 2, "Two" },
	{ 3, "Three" }
};

Console.WriteLine(dict[4]); //returns Four
Console.WriteLine(dict[2]); // returns Two

Đây là kết quả khi biên dịch và chạy chương trình:

Four
Two

Lưu ý: Indexer lấy khóa làm tham số. Nếu khóa được chỉ định không tồn tại thì sẽ ném ra ngoại lệ KeyNotFoundException.

Nếu bạn không chắc chắn về khóa thì hãy sử dụng phương thức TryGetValue(). Phương thức TryGetValue() sẽ trả về false nếu không tìm thấy khóa thay vì ném ngoại lệ.

Dictionary<int, string> dict = new Dictionary<int, string>()
{
	{ 4, "Four" },
	{ 2, "Two" },
	{ 3, "Three" }
};

string result;

if(dict.TryGetValue(1, out result))
{
    Console.WriteLine(result);
}
else
{
    Console.WriteLine("Could not find the specified key.");
}

Đây là kết quả khi biên dịch và chạy chương trình:

Could not find the specified key.

Kiểm tra phần tử tồn tại trong Dictionary

Từ điển có nhiều phương thức khác nhau để xác định xem một phần tử hoặc khóa được chỉ định có tồn tại trong từ điển hay không.

Sử dụng phương thức ContainsKey() để kiểm tra xem một khóa được chỉ định có tồn tại trong từ điển hay không.

Sử dụng phương thức Contains() để kiểm tra xem cặp Khóa và Giá trị được chỉ định có tồn tại trong từ điển hay không.

Dictionary<int, string> dict = new Dictionary<int, string>()
{
	{4, "Four"},
	{2, "Two"},
	{3, "Three"}
};

dict.ContainsKey(1); // returns false
dict.ContainsKey(4); // returns true

dict.Contains(new KeyValuePair<int,string>(2,"Two")); // returns true

Một nạp chồng của phương thức Contains() lấy IEqualityComperer làm tham số thứ hai. Một phiên bản của IEqualityComparer được sử dụng khi bạn muốn tùy chỉnh biểu thức so sánh.

Hãy xem ví dụ sau đây về một từ điển lưu trữ các đối tượng Student.

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class StudentDictionaryComparer : IEqualityComparer<KeyValuePair<int,Student>>
{
    public bool Equals(KeyValuePair<int, Student> x, KeyValuePair<int, Student> y)
    {
        return x.Key == y.Key && 
			(x.Value.Id == y.Value.Id) && 
			(x.Value.Name == y.Value.Name);
    }

    public int GetHashCode(KeyValuePair<int, Student> obj)
    {
        return obj.Key.GetHashCode();
    }
}

class Program
{
    static void Main(string[] args)
    {
        IDictionary<int, Student> studentDict = new Dictionary<int, Student>()
		{
			{ 1, new Student(){ Id =1, Name = "Bill"}},
			{ 2, new Student(){ Id =2, Name = "Steve"}},
			{ 3, new Student(){ Id =3, Name = "Ram"}}
		};

        Student std = new Student() { Id = 1, Name = "Bill" };

        KeyValuePair<int, Student> elementToFind = new KeyValuePair<int, Student>(1, std);

        bool result = studentDict.Contains(elementToFind, new StudentDictionaryComparer()); // returns true
    
        Console.WriteLine(result);
    }
}

Đây là kết quả khi biên dịch và chạy chương trình:

true

Trong ví dụ trên, chúng tôi đã sử dụng StudentDictionaryComparer triển khai thực hiện interface IEqualityComparer để so sánh các đối tượng Student trong từ điển. Bộ so sánh mặc định sẽ chỉ hoạt động với các kiểu dữ liệu nguyên thủy.

Xóa các phần tử trong Dictionary

Sử dụng phương thức Remove() để xóa một phần tử hiện có khỏi từ điển. Phương thức Remove() có hai nạp chồng phương thức, một phương thức chấp nhận một khóa và một phương thức khác chấp nhận struct KeyValuePair<TKey,TValue> làm tham số.

Ví dụ sau minh họa xóa phần tử trong Dictionary sử dụng phương thức Remove với khóa là tham số:

Dictionary<int, string> dict = new Dictionary<int, string>()
{
	{1,"One"},
	{2, "Two"},
	{3,"Three"}
};

dict.Remove(1); // removes the item which has 1 as a key

Với phương thức Remove() sử dụng KeyValuePair<TKey,TValue> làm tham số thì cả Khóa và Giá trị chỉ định phải khớp thì mới có thể xóa một phần tử. Ví dụ sau sẽ không xóa bất kỳ phần tử nào:

// removes nothing because value Two1 is not matching
dict.Remove(new KeyValuePair<int, string>(2, "Two1")); 

Sử dụng generic collection SortedDictionary nếu bạn muốn sắp xếp các phần tử trong từ điển dựa trên khóa.

Những điểm cần nhớ:

  • Dictionary lưu trữ các cặp Khóa-Giá trị trong đó khóa phải là duy nhất.
  • Trước khi thêm KeyValuePair vào từ điển, hãy kiểm tra xem khóa đó không tồn tại bằng phương thức ContainsKey().
  • Sử dụng phương thức TryGetValue() để lấy giá trị của khóa để tránh các ngoại lệ khi thực thi có thể xảy ra.
  • Sử dụng vòng lặp foreach hoặc for để duyệt qua tất cả phần tử của từ điển.
  • Sử dụng bộ chỉ mục của từ điển để truy cập từng phần tử.
  • Sử dụng lớp tùy chỉnh xuất phát từ interface IEqualityComparer để so sánh đối tượng của lớp tùy chỉnh trong phương thức Contains().

Ở hướng dẫn tiếp theo chúng ta sẽ tìm hiểu về SortedList<TKey, TValue> - cũng là một generic collection như Dictionary<Tkey, TValue>, cũng lưu trữ các cặp khóa - giá trị.

SortedList<TKey, TValue> trong C# | Comdy
SortedList<TKey, TValue> trong C# là gì? SortedList<TKey, TValue> trong C# dùng để làm gì? Cách sử dụng SortedList<TKey, TValue> trong C#.


Bài viết liên quan:

Hướng dẫn này sẽ giúp bạn tìm hiểu về đọc ghi file (File I/O) trong C# và sử dụng các lớp tiện ích để đọc ghi file.

Reflection trong C#

  • 6 min read

Reflection trong C# là gì? Ứng dụng của Reflection trong C#. Cách khai báo và sử dụng Reflection trong C#.

Attribute trong C#

  • 7 min read

Attribute trong C# là gì? Có những loại attribute nào trong C#? Làm sao để sử dụng attribute trong C#.