CodeGym /Các khóa học /C# SELF /Tìm kiếm phần tử trong collection trên C#

Tìm kiếm phần tử trong collection trên C#

C# SELF
Mức độ, Bài học
Có sẵn

1. Khả năng tìm kiếm cơ bản trong collection

Nhiều khi từ “tìm kiếm” và “lọc” nghe giống nhau, nhưng trong lập trình thì nó khác nhau đó.

  • Lọc — là khi mình muốn lấy tất cả phần tử trong collection mà thỏa mãn điều kiện nào đó (ví dụ, tất cả số lớn hơn 10).
  • Tìm kiếm — thường là mình chỉ cần một phần tử: cái đầu tiên phù hợp, phần tử có giá trị xác định, hoặc chỉ cần biết nó có trong collection không thôi.

Ví dụ như thư viện: lọc — giống như gom hết sách về chủ đề “Vũ trụ”, còn tìm kiếm — là hỏi: “Có cuốn ‘Ulysses’ không?” hoặc “Cuốn đầu tiên bìa xanh nằm ở đâu?”.

Tất cả collection chính của .NET (array, list, set, dictionary, v.v.) đều hỗ trợ nhiều cách tìm phần tử hoặc kiểm tra có tồn tại không.

Giờ mình cùng xem mấy method phổ biến nhất nha:

Collection Kiểm tra tồn tại Tìm index Tìm phần tử Tìm theo key
List<T>
Contains
IndexOf
Find
-
T[]
(mảng)
Contains
/
Array.IndexOf
Array.IndexOf
- -
HashSet<T>
Contains
- - -
Dictionary<TKey,V>
ContainsKey
,
ContainsValue
- - Có indexer
[key]

Một số method chỉ có ở một số loại collection thôi nha.

2. Tìm kiếm trong list: List<T>

List<T> là một trong những collection đa năng nhất để lưu trữ phần tử và truy cập tự do. Đây là mấy method tìm kiếm chính:

Kiểm tra có phần tử không: Contains

Method này cho biết phần tử cần tìm có trong list không:

List<string> fruits = new List<string> { "Táo", "Chuối", "Kiwi" };
bool hasKiwi = fruits.Contains("Kiwi"); // true
bool hasMango = fruits.Contains("Xoài"); // false
Console.WriteLine(hasKiwi); // True

Bên trong, Contains chỉ đơn giản duyệt qua collection, dùng method Equals.

Tìm index: IndexOf

Nếu muốn biết phần tử nằm ở đâu (số thứ tự trong list):

int index = fruits.IndexOf("Chuối"); // 1 (bắt đầu từ 0)
int absentIndex = fruits.IndexOf("Dưa hấu"); // -1 (nếu không có)
Console.WriteLine(index);
Console.WriteLine(absentIndex);

Tìm phần tử đầu tiên phù hợp: Find

Có thể tìm không phải theo giá trị cụ thể mà theo điều kiện! Dùng method Find (hoặc bạn của nó — FindIndex):

// Tìm trái cây đầu tiên có tên dài hơn 4 ký tự
string longFruit = fruits.Find(fruit => fruit.Length > 4); // "Táo"
Console.WriteLine(longFruit);

Lưu ý: nếu không tìm thấy gì, sẽ trả về giá trị mặc định của kiểu đó (null với reference type).

Tìm nhiều phần tử: FindAll

Nếu muốn lọc (tất cả phần tử phù hợp), dùng FindAll:

// Tất cả trái cây có chữ 'i' trong tên
List<string> withI = fruits.FindAll(f => f.Contains('i'));
foreach (var fruit in withI)
    Console.WriteLine(fruit); // "Kiwi"

3. Tìm kiếm trong mảng: method của class Array

Array không có method Find như List<T>, nhưng có class static Array cho nó:

int[] numbers = { 1, 2, 3, 2, 4 };
int pos = Array.IndexOf(numbers, 2); // 1 — index đầu tiên của số 2
int lastPos = Array.LastIndexOf(numbers, 2); // 3 — index cuối cùng
Console.WriteLine(pos + ", " + lastPos);

Nếu muốn tìm phần tử theo điều kiện, có thể dùng vòng lặp đơn giản:

int firstGreaterThanTwo = -1;
for (int i = 0; i < numbers.Length; i++)
{
    if (numbers[i] > 2)
    {
        firstGreaterThanTwo = numbers[i];
        break;
    }
}
Console.WriteLine(firstGreaterThanTwo); // 3

4. Method chung cho mọi collection

Nhiều collection có method tìm kiếm (ví dụ Contains, IndexOf, Find v.v.). Nếu cần gì phức tạp hơn, cứ viết vòng lặp duyệt phần tử thôi.

Ví dụ: Kiểm tra có trái cây bắt đầu bằng "Ch" không

bool hasB = false;
foreach (var f in fruits)
{
    if (f.StartsWith("Ch"))
    {
        hasB = true;
        break;
    }
}
Console.WriteLine(hasB); // True

Ví dụ: Tìm trái cây đầu tiên chứa "i"

string withI = null;
foreach (var f in fruits)
{
    if (f.Contains('i'))
    {
        withI = f;
        break;
    }
}
Console.WriteLine(withI); // "Kiwi"

Ví dụ: Tìm kiếm theo object tự định nghĩa

class Student
{
    public string Name;
    public int Group;
    public int Id;
}

List<Student> students = new List<Student>
{
    new Student { Name = "Ivan", Group = 101, Id = 1 },
    new Student { Name = "Maria", Group = 101, Id = 2 },
    new Student { Name = "Petr", Group = 102, Id = 3 },
};

// Tìm student có Id == 2
Student maria = null;
foreach (var s in students)
{
    if (s.Id == 2)
    {
        maria = s;
        break;
    }
}
if (maria != null)
    Console.WriteLine(maria.Name); // "Maria"
else
    Console.WriteLine("Không tìm thấy student");

5. Tìm kiếm trong HashSet<T>: chỉ “Có hay không?”

Set (HashSet<T>) sinh ra để kiểm tra nhanh “có cái này không”. Không tìm được theo index, nhưng kiểm tra tồn tại thì siêu nhanh:

HashSet<int> set = new HashSet<int> { 1, 3, 5, 7 };
bool hasThree = set.Contains(3); // True
Console.WriteLine(hasThree);

// Nếu muốn tìm theo điều kiện (ví dụ có số chẵn không):
bool hasEven = false;
foreach (var x in set)
{
    if (x % 2 == 0)
    {
        hasEven = true;
        break;
    }
}
Console.WriteLine(hasEven); // False

6. Tìm kiếm trong dictionary: Dictionary<TKey, TValue>

Dictionary — collection của cặp “key-value”. Tìm theo key là điểm mạnh nhất của nó.

Kiểm tra có key không

Dictionary<int, string> idToName = new Dictionary<int, string>
{
    { 1, "Vasya" }, { 2, "Katya" }
};
if (idToName.ContainsKey(2))
    Console.WriteLine(idToName[2]); // "Katya"

Tìm value theo key: an toàn!

if (idToName.TryGetValue(3, out string result))
    Console.WriteLine(result);
else
    Console.WriteLine("Không có student với Id này"); // In ra dòng này

Tìm theo value (hiếm và chậm):

bool containsVasya = idToName.ContainsValue("Vasya");
Console.WriteLine(containsVasya); // True

Tìm entry theo điều kiện trên value hoặc key

// Id đầu tiên mà tên bắt đầu bằng "K"
KeyValuePair<int, string> entry = default;
bool found = false;
foreach (var pair in idToName)
{
    if (pair.Value.StartsWith("K"))
    {
        entry = pair;
        found = true;
        break;
    }
}
if (found)
    Console.WriteLine($"{entry.Key}: {entry.Value}"); // "2: Katya"

7. Một số lưu ý hay ho

Bảng method tìm kiếm

Loại collection Kiểm tra tồn tại Contains Tìm index IndexOf Tìm theo điều kiện Find Tìm key an toàn TryGetValue
List<T>
Có Có Có (
Find
)
-
T[]
(mảng)
Có (
Array.Contains
/vòng lặp)
Có (
Array.IndexOf
)
Vòng lặp -
HashSet<T>
Có - Không (chỉ tự viết) -
Dictionary<TKey,V>
Có (
ContainsKey
)
- Tự viết theo key/value Có

Tìm kiếm lặp: tự code luôn

Cho vui, thử tự viết method tìm kiếm cho collection. Ví dụ, tìm index phần tử đầu tiên lớn hơn giá trị cho trước:

static int FindFirstGreaterIndex(IEnumerable<int> collection, int minValue)
{
    int index = 0;
    foreach (var item in collection)
    {
        if (item > minValue)
            return index;
        index++;
    }
    return -1; // không tìm thấy
}

var nums = new List<int> { 1, 4, 7, 2 };
Console.WriteLine(FindFirstGreaterIndex(nums, 3)); // 1 (số 4)

Ở đây không phụ thuộc vào loại (List<T>, int[], thậm chí HashSet<T>), cũng không quan tâm bên trong nó thế nào.

Ứng dụng tìm kiếm trong thực tế

  • Tìm user theo login hoặc e-mail trong database.
  • Kiểm tra trong giỏ hàng có sản phẩm này không.
  • Tìm nhanh setting theo tên tham số.
  • Kiểm tra đã thực hiện bước nào đó chưa (ví dụ trong workflow).
  • Tìm vị trí lỗi trong mảng log.

Phỏng vấn middle-dev kiểu gì cũng hỏi: “Tìm phần tử trong collection như nào? Viết method trả về phần tử đầu tiên/tất cả/index theo điều kiện đi?”. Nên luyện tìm kiếm là cực kỳ hữu ích luôn!

8. Lỗi thường gặp và đặc điểm khi tìm kiếm

Quan trọng: method tìm kiếm phụ thuộc vào cách so sánh object. Ví dụ, nếu thêm class tự định nghĩa vào list, mặc định sẽ so sánh theo reference! Muốn tìm kiếm “theo nội dung”, phải override method Equals và GetHashCode (cái này sẽ nói kỹ ở bài sau).

Ví dụ lỗi:

var s1 = new Student { Name = "Egor", Id = 42 };
students.Add(s1);
// Giờ tạo object mới với cùng Id và tên:
var s2 = new Student { Name = "Egor", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False (!)

Compiler so sánh không phải theo field, mà theo reference (đây là hai object khác nhau).

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Tìm phần tử phù hợp đầu tiên trong danh sách
Tìm phần tử phù hợp đầu tiên trong danh sách
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION