JavaRush /Курсы /C# SELF /Поиск элементов в коллекциях на C#

Поиск элементов в коллекциях на C#

C# SELF
29 уровень , 1 лекция
Открыта

1. Базовые возможности поиска в коллекциях

Зачастую слова “поиск” и “фильтрация” воспринимаются как синонимы, но в программировании они всё же различаются.

  • Фильтрация — это когда мы хотим получить все элементы коллекции, которые соответствуют некоторому условию (например, все числа больше 10).
  • Поиск — когда нам чаще всего нужен какой-то один элемент: первый подходящий, элемент с определённым значением, либо только узнать, есть ли он вообще в коллекции.

Если провести аналогию с библиотекой: фильтрация — как собрать все книги по теме “Космос”, а поиск — это спросить: “Есть у вас книга ‘Улисс’?” или “Где стоит первая книга с синим переплётом?”.

Все основные коллекции .NET (массивы, списки, множества, словари и т.д.) поддерживают несколько вариантов поиска элементов или проверки наличия.

Давайте разберём самые распространённые методы:

Коллекция Проверить наличие Найти индекс Найти элемент Найти по ключу
List<T>
Contains
IndexOf
Find
-
T[]
(массив)
Contains
/
Array.IndexOf
Array.IndexOf
- -
HashSet<T>
Contains
- - -
Dictionary<TKey,V>
ContainsKey
,
ContainsValue
- - Имеет индексатор
[key]

Некоторые методы доступны только на конкретных типах.

2. Поиск в списках: List<T>

List<T> — одна из самых универсальных коллекций для хранения элементов с произвольным доступом. Вот основные методы поиска:

Проверка наличия элемента: Contains

Этот метод сообщает, содержится ли нужный элемент в списке:

List<string> fruits = new List<string> { "Яблоко", "Банан", "Киви" };
bool hasKiwi = fruits.Contains("Киви"); // true
bool hasMango = fruits.Contains("Манго"); // false
Console.WriteLine(hasKiwi); // True

Под капотом Contains просто перебирает коллекцию, используя метод Equals.

Поиск индекса: IndexOf

Если нужно узнать, где именно находится элемент (его номер в списке):

int index = fruits.IndexOf("Банан"); // 1 (индексация с нуля)
int absentIndex = fruits.IndexOf("Арбуз"); // -1 (если нет элемента)
Console.WriteLine(index);
Console.WriteLine(absentIndex);

Найти первый подходящий: Find

Можно искать не по конкретному значению, а по условию! Для этого используется метод Find (или его индексный друг — FindIndex):

// Найти первый фрукт, у которого название длиннее 4 символов
string longFruit = fruits.Find(fruit => fruit.Length > 4); // "Яблоко"
Console.WriteLine(longFruit);

Обратите внимание: если ничего не найдено, возвращается значение по умолчанию для типа (null для ссылочных).

Поиск нескольких элементов: FindAll

Если нужна именно фильтрация (все подходящие), используйте FindAll:

// Все фрукты, в названии которых есть буква 'и'
List<string> withI = fruits.FindAll(f => f.Contains('и'));
foreach (var fruit in withI)
    Console.WriteLine(fruit); // "Киви"

3. Поиск в массивах: методы класса Array

Массивы не имеют методов Find внутри себя (как List<T>), но для них есть статический класс Array:

int[] numbers = { 1, 2, 3, 2, 4 };
int pos = Array.IndexOf(numbers, 2); // 1 — первый индекс 2-ки
int lastPos = Array.LastIndexOf(numbers, 2); // 3 — последний индекс
Console.WriteLine(pos + ", " + lastPos);

Если нужно найти элемент по условию, можно использовать простой цикл:

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

4. Универсальные методы для всех коллекций

Многие коллекции предоставляют методы для поиска (например, Contains, IndexOf, Find и др.). Если требуется что-то более сложное, пишется цикл перебора элементов.

Пример: Проверить, есть ли фрукт, начинающийся с "Б"

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

Пример: Найти первый фрукт, содержащий "и"

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

Пример: Поиск по пользовательским объектам

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

List<Student> students = new List<Student>
{
    new Student { Name = "Иван", Group = 101, Id = 1 },
    new Student { Name = "Мария", Group = 101, Id = 2 },
    new Student { Name = "Пётр", Group = 102, Id = 3 },
};

// Ищем студента с Id == 2
Student maria = null;
foreach (var s in students)
{
    if (s.Id == 2)
    {
        maria = s;
        break;
    }
}
if (maria != null)
    Console.WriteLine(maria.Name); // "Мария"
else
    Console.WriteLine("Студент не найден");

5. Поиск в HashSet<T>: только “Есть или нет?”

Множества (HashSet<T>) созданы для быстрого поиска “есть такое или нет”. Они не обеспечивают поиск по индексу, но зато очень быстро проверяют наличие:

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

// Если нужен поиск по условию (например, есть ли чётные числа):
bool hasEven = false;
foreach (var x in set)
{
    if (x % 2 == 0)
    {
        hasEven = true;
        break;
    }
}
Console.WriteLine(hasEven); // False

6. Поиск в словарях: Dictionary<TKey, TValue>

Словарь — коллекция пар “ключ-значение”. Поиск по ключу — это его суперсила.

Проверка наличия ключа

Dictionary<int, string> idToName = new Dictionary<int, string>
{
    { 1, "Вася" }, { 2, "Катя" }
};
if (idToName.ContainsKey(2))
    Console.WriteLine(idToName[2]); // "Катя"

Поиск значения по ключу: безопасно!

if (idToName.TryGetValue(3, out string result))
    Console.WriteLine(result);
else
    Console.WriteLine("Нет студента с таким Id"); // Это и выведется

Поиск по значению (редко и медленно):

bool containsVasya = idToName.ContainsValue("Вася");
Console.WriteLine(containsVasya); // True

Найти запись по условию на значение или ключ

// Первый Id, где имя начинается с "К"
KeyValuePair<int, string> entry = default;
bool found = false;
foreach (var pair in idToName)
{
    if (pair.Value.StartsWith("К"))
    {
        entry = pair;
        found = true;
        break;
    }
}
if (found)
    Console.WriteLine($"{entry.Key}: {entry.Value}"); // "2: Катя"

7. Полезные нюансы

Таблица методов поиска

Тип коллекции Проверить наличие Contains Найти индекс IndexOf Найти по условию Find Безопасный поиск по ключу TryGetValue
List<T>
Да Да Да (
Find
)
-
T[]
(массив)
Да (
Array.Contains
/цикл)
Да (
Array.IndexOf
)
Цикл -
HashSet<T>
Да - Нет (только вручную) -
Dictionary<TKey,V>
Да (
ContainsKey
)
- Вручную по ключу/значению Да

Итеративный поиск: пишем вручную

Для полного погружения попробуем сами написать метод поиска для коллекции. Например, найти индекс первого элемента, который больше заданного значения:

static int FindFirstGreaterIndex(IEnumerable<int> collection, int minValue)
{
    int index = 0;
    foreach (var item in collection)
    {
        if (item > minValue)
            return index;
        index++;
    }
    return -1; // не нашли
}

var nums = new List<int> { 1, 4, 7, 2 };
Console.WriteLine(FindFirstGreaterIndex(nums, 3)); // 1 (число 4)

Здесь мы не привязаны ни к типу (List<T>, int[], даже HashSet<T>), ни ко внутренней реализации.

Применение поиска в реальных задачах

  • Поиск пользователя по логину или e-mail в базе данных.
  • Проверка, есть ли в корзине покупок данный товар.
  • Быстрый поиск настроек по имени параметра.
  • Проверка, был ли уже выполнен определённый шаг (например, в workflow).
  • Поиск позиции ошибки в массиве логов.

Большинство собеседований с middle-разработчиками обязательно включает вопрос: “Как искать элементы в коллекции? Как бы вы написали метод, который возвращает первый/все/индекс элемента по условию?”. Так что практика поиска — вещь крайне полезная!

8. Типичные ошибки и особенности поиска

Важный момент: методы поиска зависят от того, как сравниваются объекты. Например, если в список добавлять свои классы, сравнение по умолчанию — это сравнение ссылок! Чтобы поиск работал “по содержимому”, нужно переопределять методы Equals и GetHashCode (об этом подробнее в следующих лекциях).

Пример проблемы:

var s1 = new Student { Name = "Егор", Id = 42 };
students.Add(s1);
// Теперь создаём новый объект с тем же Id и именем:
var s2 = new Student { Name = "Егор", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False (!)

Компилятор сравнивает не по полям, а по ссылке (это разные объекты).

2
Задача
C# SELF, 29 уровень, 1 лекция
Недоступна
Поиск первого подходящего элемента в списке
Поиск первого подходящего элемента в списке
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 39
10 февраля 2026
В задаче "правильное решение" - неправильное. Метод Find() никогда не возвращает null для значащих типов. Он возвращает дефолтное значение, которое для int будет 0.
Slevin Уровень 39
10 февраля 2026
Множества (HashSet<T>) созданы для быстрого поиска “есть такое или нет”. Они не обеспечивают поиск по индексу, но зато очень быстро проверяют наличие: И приводится пример, который по скорости будет таким же как и для List<T>...
Александр Уровень 37
3 февраля 2026
уже и делегаты пошли в дело, а объяснение делегатов даже не в этом уровне