JavaRush /Курсы /C# SELF /Список: List<T>

Список: List<T>

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

1. Введение

Представьте, что вы ведете список покупок. Сегодня вам нужно купить 3 товара, завтра — 10, а послезавтра — всего один. Если бы вы использовали массив, то пришлось бы каждый раз создавать новый массив под конкретное количество товаров. Или создавать очень большой массив "на всякий случай", тем самым "носить" с собой уйму пустых ячеек. Звучит не очень эффективно, верно?

А теперь представьте, что у вас есть волшебный рюкзак, который сам расширяется, когда вы добавляете туда вещи, и сжимается, когда вы их вынимаете. Вам не нужно заранее знать, сколько вещей вы положите. Вы просто кладете, и рюкзак адаптируется. Вот это и есть List<T> в мире программирования на C#!

List<T> — это обобщенная коллекция, которая позволяет хранить последовательность элементов определенного типа. Ключевое слово здесь — "динамический". В отличие от массивов, List<T> может изменять свой размер во время выполнения программы. Он сам управляет своей внутренней емкостью, увеличивая ее при необходимости.

Что означает буква T в List<T>? Это так называемый параметр типа. Когда вы создаете List, вы указываете, какой тип данных он будет хранить вместо T. Например, List<int> будет хранить только целые числа, List<string> — только строки, а List<double> — только вещественные числа. Это гарантирует, что вы случайно не сможете добавить яблоко в список апельсинов, что очень важно для стабильности и предсказуемости вашего кода.

Зачем это нужно?

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

  • Список пользователей, зашедших на сайт.
  • Список товаров в корзине интернет-магазина.
  • Список результатов поиска по запросу.
  • Список задач в планировщике, который мы сейчас и будем разрабатывать.

Во всех этих случаях количество элементов может меняться, и List<T> становится нашим незаменимым помощником.

Основные методы List<T>

Метод Описание Пример
Add(T item)
Добавить элемент в конец списка
numbers.Add(5);
Insert(int idx, T)
Вставить на заданную позицию
numbers.Insert(0, 42);
Remove(T item)
Удалить первое вхождение элемента
numbers.Remove(100);
RemoveAt(int idx)
Удалить элемент по индексу
numbers.RemoveAt(2);
Clear()
Очистить список
numbers.Clear();
Contains(T item)
Проверить, есть ли в списке элемент
numbers.Contains(10);
IndexOf(T item)
Индекс первого вхождения
int pos = numbers.IndexOf(42);
Count
Количество элементов в списке
int n = numbers.Count;
Capacity
Ёмкость внутреннего буфера
numbers.Capacity = 100;

2. Создание списка (List<T>)

Приступим к практике! Представьте, что мы решили создать простенький менеджер задач. Нам нужен список, куда мы будем добавлять наши дела.

Для создания нового списка List<T> мы используем оператор new, точно так же, как и при создании других объектов.

Как объявить и создать List

Синтаксис создания списка очень похож на массив, только вместо квадратных скобок — угловые:

using System.Collections.Generic;

List<int> numbers = new List<int>();

Комментарии для тех, кто любит подробности:
- List<int> — это список целых чисел (int). Вместо int можно подставить любой другой тип: string, double, собственный класс и так далее.
- После создания список пуст — его длина равна 0.

Пример: первый шаг в развитии нашего приложения

Допустим, у нас есть приложение, в котором пользователь вводил имя и возраст (ещё с первых лекций). Давайте теперь добавим возможность сохранять несколько имён в список, чтобы потом с ними работать.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<string> names = new List<string>();

        Console.WriteLine("Введите три имени:");
        for (int i = 0; i < 3; i++)
        {
            string name = Console.ReadLine();
            names.Add(name);
        }

        Console.WriteLine("Вы ввели:");
        foreach (string n in names)
        {
            Console.WriteLine(n);
        }
    }
}

Обратите внимание на Add(). Именно этот метод добавляет в список новые элементы. По сути — делает его динамическим!

Можно создать список сразу с элементами!

List<string> planets = new List<string> { "Меркурий", "Венера", "Земля" };

Очень удобно, когда вы знаете "стартовый состав". Такой синтаксис часто применяют и в тестах, и при инициализации фиксированных данных.

3. Базовые операции с List<T>

Прежде чем мы перейдём к трюкам и магии, давайте разберём "азбуку" списка.

Добавление элементов: Add()

Использовать метод Add() — как положить новую вещь на полку:

List<int> numbers = new List<int>();
numbers.Add(42);
numbers.Add(100);

Теперь внутри: [42, 100]

Получение количества элементов: Count

В отличие от массива, у списка свойство называется не Length, а Count:

int count = numbers.Count; // Вернёт 2

Помните: у списков используется свойство Count, а у массивов и строк — Length. Это разные свойства для похожей цели.

Доступ к элементам по индексу

Два способа, оба идентичны по сути:

int first = numbers[0]; // 42
numbers[1] = 200; // Заменили 100 на 200

Важно: Индексация, как и у массивов, начинается с нуля. Не пытайтесь обратиться к numbers[3], если у вас всего три элемента!

Перебор элементов списка

Можно использовать цикл for или foreach:

// Через for — если нужен индекс
for (int i = 0; i < numbers.Count; i++)
{
    Console.WriteLine(numbers[i]);
}

// Через foreach — если индекс не нужен
foreach (int number in numbers)
{
    Console.WriteLine(number);
}

foreach обычно проще и понятнее, особенно для больших коллекций.
Но с ним вы не можете изменить элементы коллекции по индексу или досрочно выйти из цикла с помощью break, если не используете дополнительные конструкции.

Вставка элемента по индексу: Insert()

Метод Insert() нужен, если вы захотите добавить элемент не в конец списка, а, куда угодно. Например, на второе место:

numbers.Insert(1, 55); // Вставит 55 на позицию с индексом 1

Теперь numbers: [42, 55, 200]

Удаление элементов

Удалять из списка почти так же просто, как добавлять в него:

numbers.Remove(55); // Удалит ПЕРВОЕ вхождение 55
numbers.RemoveAt(0); // Удалит элемент с индексом 0
numbers.Clear(); // Полностью очищает список

Нюансы удаления:
- Если удалить несуществующее значение методом Remove, ничего плохого не случится: список не изменится, ошибок вы не получите.
- После Clear() список снова пуст.

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

Как растёт List<T>

Когда вы добавляете элементы в List<T>, он не увеличивает размер каждый раз понемногу. Вместо этого List<T> заранее выделяет блок памяти с запасом (внутренняя ёмкость — Capacity).

Как только элементов становится больше, чем текущая ёмкость, список выделяет новый, более крупный блок памяти (обычно в 2 раза больше) и копирует туда все старые значения:


Добавляем элементы → вместилось → вместилось → вместилось →
Новый элемент не вмещается → создаётся массив побольше → копируются данные → продолжаем

Это позволяет добавлять элементы быстрее, ведь память перераспределяется не при каждом Add, а лишь изредка.

var list = new List<int>();    // Capacity = 0
list.Add(1);                   // Capacity = 4
list.Add(2);                   // Capacity = 4
list.Add(3);                   // Capacity = 4
list.Add(4);                   // Capacity = 4
list.Add(5);                   // Capacity = 8 (расширение!)

Capacity vs. Count: в чём разница?

  • Count — количество элементов, добавленных в список.
  • Capacity — размер внутреннего массива, хранящего элементы.
List<int> nums = new List<int>();
Console.WriteLine(nums.Count); // 0
Console.WriteLine(nums.Capacity); // Обычно 0 или небольшой размер

nums.Add(123);
Console.WriteLine(nums.Count); // 1
Console.WriteLine(nums.Capacity); // Теперь может быть больше Count

nums.Capacity = 1000; // Можно явно увеличить Capacity, если знаете, что будет много элементов

Большинство разработчиков используют только Count, но если вы пишете требовательные к производительности приложения (например, игры или анализ данных), то тонкая настройка Capacity может быть полезной.

5. Пример

Продолжим развивать наше маленькое приложение — теперь, например, пользователь может добавлять свои любимые числа, а затем работать со списком: вставлять, удалять, выводить и очищать.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> favoriteNumbers = new List<int>();

        Console.WriteLine("Введите ваши любимые числа (по одному в строке, 0 — завершить):");
        while (true)
        {
            int num = int.Parse(Console.ReadLine());
            if (num == 0) break;
            favoriteNumbers.Add(num);
        }

        Console.WriteLine("Ваши любимые числа:");
        foreach (int n in favoriteNumbers)
        {
            Console.Write(n + " ");
        }

        Console.WriteLine("\nХотите удалить первое число? (y/n)");
        if (Console.ReadLine().ToLower() == "y")
        {
            favoriteNumbers.RemoveAt(0);
        }

        Console.WriteLine("А теперь ваш список:");
        foreach (int n in favoriteNumbers)
        {
            Console.Write(n + " ");
        }
    }
}

Замечание: Здесь мы используем int.Parse. В реальных проектах лучше всегда проверять ввод на корректность через int.TryParse, чтобы не "ронять" программу на любой ошибке пользователя.

6. Частые ошибки и "грабли" начинающих

Очень популярен такой сценарий: вы пишете цикл от 0 до List.Count, но в процессе цикла (например, в foreach) меняете сам список (добавляете или удаляете элементы). Не делайте так! Элита программирования называет это "модификация коллекции во время перебора". В большинстве случаев вы сразу получите исключение типа InvalidOperationException.

Пример плохого кода:

foreach (int n in numbers)
{
    if (n < 0)
        numbers.Remove(n); // БУМ! Runtime error.
}

Правильно: или сначала собрать индексы/элементы для удаления в отдельный список, или воспользоваться циклом for с обратным направлением.

2
Задача
C# SELF, 27 уровень, 2 лекция
Недоступна
Работа со строками в List<T>
Работа со строками в List<T>
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ