← Назад к вопросам

Какие структуры данных вы знаете? В чем разница между Array, List, Dictionary, HashSet?

1.6 Junior🔥 111 комментариев
#C# и ООП

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Основные структуры данных в C# и Unity

В контексте C# и Unity мы чаще всего работаем с коллекциями из пространства имен System.Collections.Generic. Они являются типизированными и безопасными.

1. Array (Массив)

Массив — это простейшая и самая низкоуровневая структура. Это фиксированный, непрерывный блок памяти для элементов одного типа.

  • Фиксированный размер: Количество элементов задается при создании и не может быть изменено.
  • Прямой доступ по индексу: Очень быстрый (O(1)), так как адрес элемента вычисляется как начальный адрес + (индекс * размер_элемента).
  • Минимальные накладные расходы на память.
// Объявление и инициализация
int[] enemyHealths = new int[100];
Vector3[] waypoints = new Vector3[] { pointA, pointB, pointC };

// Молниеносный доступ
float firstHealth = enemyHealths[0];

В Unity используют для статических данных, известных на этапе компиляции или инициализации: сетки вершин (Mesh.vertices), предопределенные наборы точек (пути, патрули), пулы объектов фиксированного размера.

2. List<T> (Список)

List<T> — это динамический массив. Внутри он использует обычный массив, но при нехватке места автоматически создает новый массив большего размера и копирует в него старые элементы.

  • Динамический размер: Может расти и сокращаться (Add(), Remove()).
  • Быстрый доступ по индексу (как у массива).
  • Относительно медленные вставка/удаление в середину, так как требуют сдвига последующих элементов (O(n)).
  • Допускает дубликаты и сохраняет порядок добавления.
List<Enemy> activeEnemies = new List<Enemy>();

void SpawnEnemy() {
    activeEnemies.Add(Instantiate(enemyPrefab)); // Автоматическое расширение
}

void Update() {
    for (int i = 0; i < activeEnemies.Count; i++) { // Быстрая итерация
        activeEnemies[i].Move();
    }
}

В Unity это "рабочая лошадка". Идеально подходит для коллекций объектов, размер которых часто меняется: список врагов на уровне, инвентарь, очередь сообщений. Всегда предпочтительнее ArrayList из-за типобезопасности.

3. Dictionary<TKey, TValue> (Словарь)

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

  • Сверхбыстрый доступ по ключу (в среднем O(1)). Поиск не требует перебора, а основан на вычислении хеш-кода ключа.
  • Нет гарантированного порядка элементов (хотя внешне может выглядеть упорядоченным).
  • Потребляет больше памяти, чем List, из-за хранения хеш-таблицы.
  • Проверка на наличие ключа (ContainsKey) также очень быстрая.
Dictionary<string, Weapon> weaponCache = new Dictionary<string, Weapon>();

void LoadWeapon(string weaponId) {
    if (!weaponCache.ContainsKey(weaponId)) {
        weaponCache[weaponId] = Resources.Load<Weapon>($"Weapons/{weaponId}");
    }
    return weaponCache[weaponId];
}

// Прямой доступ по ID. Не нужно перебирать все оружие.
Weapon rifle = weaponCache["assault_rifle_01"];

В Unity используют для кеширования ресурсов (Resources.Load), быстрого поиска объектов по уникальному идентификатору (ID игрока, название предмета), конфигураций.

4. HashSet<T> (Множество)

HashSet — это коллекция уникальных элементов, также основанная на хеш-таблице. По сути, это Dictionary без значений, только ключи.

  • Хранит только уникальные значения. Попытка добавить дубликат игнорируется.
  • Чрезвычайно быстрая проверка на наличие элемента (Contains) — O(1).
  • Быстрые операции над множествами: объединение (UnionWith), пересечение (IntersectWith), разность (ExceptWith).
  • Порядок элементов не определен.
HashSet<Player> playersInSafeZone = new HashSet<Player>();

void OnTriggerEnter(Collider other) {
    Player p = other.GetComponent<Player>();
    if (p != null) {
        playersInSafeZone.Add(p); // Добавится, только если его еще нет внутри
    }
}

bool IsPlayerSafe(Player player) {
    // Мгновенная проверка, независимо от количества игроков в зоне
    return playersInSafeZone.Contains(player);
}

В Unity используют для устранения дубликатов, отслеживания состояния (игроки в зоне, примененные баффы), быстрых проверок принадлежности к набору.

Сравнение и ключевые отличия

КритерийArrayList<T>Dictionary<K,V>HashSet<T>
Основное назначениеФиксированная коллекцияДинамическая коллекцияПоиск по ключуПроверка уникальности
ИндексацияПо целочисленному индексуПо целочисленному индексуПо произвольному ключуНет индексации
ПорядокСохраняетсяСохраняетсяНе гарантированНе гарантирован
ДубликатыРазрешеныРазрешеныКлючи — нетЗапрещены
Быстрая операцияДоступ по индексуДоступ по индексу, Add в конецПоиск/добавление по ключуПроверка Contains
Медленная операцияИзменение размераВставка/удаление в серединуПеребор всех элементов (медленнее List)Перебор/получение по порядку
ПамятьМинимумНемного больше ArrayБольше (хеш-таблица)Больше (хеш-таблица)

Заключение для Unity-разработчика

Выбор структуры в Unity — это почти всегда компромисс между удобством и производительностью.

  • Для статичных данных или пулов объектов используй Array.
  • Для динамических коллекций игровых объектов (враги, снаряды) — List<T>.
  • Если нужен мгновенный поиск по уникальному ID, строке или объектуDictionary<K,V>.
  • Чтобы гарантировать уникальность или очень быстро проверять наличиеHashSet<T>.

Помни о профилировании. В 90% случаев List будет достаточно, но при работе с тысячами объектов или в критичных к производительности методах (например, Update) правильный выбор между Dictionary и линейным поиском по List может дать прирост в несколько миллисекунд на кадр.

Какие структуры данных вы знаете? В чем разница между Array, List, Dictionary, HashSet? | PrepBro