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); // БАХ! Помилка під час виконання.
}

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

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ