Списки, множества и карты

Одна переменная хранит одно значение; коллекция хранит сразу множество — список товаров, набор тегов, словарь настроек.

Суть: List — упорядоченный список с дубликатами, Set — набор уникальных значений без порядка, Map — пары «ключ — значение». Это три рабочие лошадки любого приложения.

Коллекции в Dart умеют создаваться декларативно прямо внутри литерала. Есть collection if и collection for: можно написать [if (isAdmin) adminButton, for (var t in tags) Chip(t)] — и список соберётся по условию и циклу прямо на месте. Эта возможность бесценна во Flutter, где списки детей виджета часто зависят от данных и флагов. Так данные превращаются в интерфейс без отдельных циклов снаружи.

Представьте экран со списком задач. Каждая задача — отдельный объект, но их много, и хранить сотню переменных task1, task2 невозможно. Поэтому данные складывают в коллекции. Самая частая — List: упорядоченная последовательность, где у каждого элемента есть индекс, начинающийся с нуля.

List<String> fruits = ['яблоко', 'банан', 'груша'];
print(fruits[0]);        // яблоко (индекс с нуля)
print(fruits.length);    // 3
fruits.add('слива');     // добавить в конец

Set<int> ids = {1, 2, 2, 3};   // дубликат 2 исчезнет
print(ids);              // {1, 2, 3}

Map<String, int> ages = {'Аня': 20, 'Боря': 25};
print(ages['Аня']);      // 20
ages['Вера'] = 30;       // добавить пару

Угловые скобки List<String> — это дженерик: они говорят, что в списке лежат именно строки. Компилятор не даст положить туда число — ещё один уровень защиты от ошибок.

Как выбрать структуру под капотом

Каждая коллекция оптимизирована под свою задачу. List хранит порядок и допускает повторы — идеален для ленты постов. Set мгновенно проверяет, есть ли элемент, и сам выбрасывает дубликаты — идеален для набора уникальных тегов. Map ищет значение по ключу за один шаг — идеален для настроек или кэша.

  List   [a, b, c, a]    порядок есть, дубликаты можно, доступ по индексу
           0  1  2  3

  Set    {a, b, c}       порядка нет, дубликатов нет, быстрый contains()

  Map    {k1: v1,        ключ -> значение, быстрый поиск по ключу
          k2: v2}
# Аналоги коллекций Dart на Python: list / set / dict
fruits = ['яблоко', 'банан', 'груша']
print(fruits[0], len(fruits))     # доступ по индексу и длина

ids = {1, 2, 2, 3}                # дубликат уберётся
print('set:', ids)

ages = {'Аня': 20, 'Боря': 25}
ages['Вера'] = 30                 # добавить пару
print('Аня:', ages['Аня'])
print('Есть Боря?', 'Боря' in ages)

Частые ошибки

  • Выход за границы списка. fruits[10] при трёх элементах — ошибка. Индекс последнего элемента всегда length - 1.
  • Ожидать порядок от Set. Множество не гарантирует порядок — если он важен, берите List.
  • Обращаться к несуществующему ключу Map. ages['Кто-то'] вернёт null, а не упадёт — учитывайте это в null-safety.

Best practices

  • Всегда указывайте тип элементов: List<Task>, а не просто List — так вы получаете подсказки и защиту.
  • Нужна уникальность — берите Set, не изобретайте проверку дубликатов вручную.
  • Для неизменяемых наборов используйте const-коллекции — Flutter их оптимизирует.

При выборе между структурами думайте о главном вопросе к данным. Если вы будете часто спрашивать «есть ли такой элемент?» — берите Set или Map, они отвечают мгновенно. Если важен порядок и индекс — List. Если нужно связать одно с другим (id с объектом, ключ с настройкой) — однозначно Map. Правильно выбранная структура не только ускоряет код, но и делает его намерение очевидным для читающего.

Итог: List, Set и Map покрывают почти все потребности в хранении данных. Выбор правильной структуры — это половина чистого кода; следующая половина — умение её обрабатывать, чем мы займёмся в следующем уроке.

Проверьте себя
1. Какая коллекция автоматически убирает дубликаты значений?
AList
BSet
CMap
DString
2. С какого индекса начинается доступ к элементам List в Dart?
AС 1
BС 0
CС -1
DС length