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

Какие коллекции используешь?

1.0 Junior🔥 212 комментариев
#C# и ООП#Коллекции и структуры данных

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

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

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

Используемые коллекции в Unity/C# разработке

В Unity-разработке я активно использую весь спектр коллекций из System.Collections, System.Collections.Generic и System.Collections.Concurrent пространств имен, выбирая их в зависимости от конкретных задач производительности, потокобезопасности и семантики данных.

Основные типы коллекций

1. Generic коллекции (наиболее часто используемые):

// List<T> - основная рабочая лошадка для динамических массивов
List<GameObject> enemies = new List<GameObject>();
enemies.Add(enemyPrefab);
enemies.RemoveAt(0);

// Dictionary<TKey, TValue> - для быстрого поиска по ключу
Dictionary<string, PlayerData> players = new Dictionary<string, PlayerData>();
players["player1"] = new PlayerData();
if (players.ContainsKey("player2")) { /* ... */ }

// HashSet<T> - для уникальных элементов и операций с множествами
HashSet<int> collectedItems = new HashSet<int>();
collectedItems.Add(101);
bool isNew = collectedItems.Add(101); // false, элемент уже существует

// Queue<T> и Stack<T> - для FIFO и LIFO структур
Queue<Action> actionQueue = new Queue<Action>();
Stack<Transform> undoStack = new Stack<Transform>();

2. Специализированные Unity-коллекции:

// Массивы - для фиксированных данных, известных на этапе компиляции
public GameObject[] waypoints; // Назначается в инспекторе

// Unity-специфичные коллекции для оптимизации
using Unity.Collections; // Для низкоуровневых операций
using UnityEngine.Pool; // Object Pooling для оптимизации инстанциирования

Критерии выбора коллекций

Я выбираю коллекции по следующим параметрам:

  • Частота добавления/удаления элементов:

    • Частые изменения: List<T> или LinkedList<T>
    • Редкие изменения: массивы
  • Необходимость поиска:

    • По ключу: Dictionary<TKey, TValue> (O(1) в среднем)
    • По значению: HashSet<T> или List<T> с бинарным поиском после сортировки
  • Порядок элементов:

    • Важен порядок добавления: List<T>, Queue<T>
    • Нужен доступ к обоим концам: LinkedList<T>
    • LIFO/FIFO: Stack<T>/Queue<T>
  • Потокобезопасность:

    • Многопоточность: коллекции из System.Collections.Concurrent
    • Один поток: стандартные generic-коллекции

Паттерны использования в Unity

Для GameObject и компонентов:

// Кэширование компонентов
private Dictionary<Type, Component> _componentCache = new Dictionary<Type, Component>();

public T GetCachedComponent<T>() where T : Component
{
    Type type = typeof(T);
    if (!_componentCache.ContainsKey(type))
    {
        _componentCache[type] = GetComponent<T>();
    }
    return (T)_componentCache[type];
}

Для оптимизации в Update-методах:

// Использование Capacity для избежания аллокаций
List<Vector3> positions = new List<Vector3>(100);
void Update()
{
    positions.Clear(); // Переиспользование коллекции
    // Заполнение positions
}

Для событий и делегатов:

private HashSet<Action> _eventHandlers = new HashSet<Action>();
// HashSet предотвращает дублирование подписчиков

Производительность и память

В performance-critical секциях (Update, FixedUpdate, рендеринг) я придерживаюсь правил:

  1. Избегаю аллокаций в циклах через предварительное выделение памяти (Capacity)
  2. Использую struct-коллекции из Unity.Collections для Burst-компиляции
  3. При работе с большими данными выбираю массивы вместо List для уменьшения оверхеда
  4. Для частых поисков использую Dictionary, даже если нужно поддерживать порядок (комбинирую с List)

Пример оптимизации:

// Плохо: аллокация в каждом кадре
List<Enemy> GetActiveEnemies()
{
    return new List<Enemy>(_allEnemies.Where(e => e.IsActive));
}

// Хорошо: переиспользование коллекции
private List<Enemy> _activeEnemiesCache = new List<Enemy>(50);

List<Enemy> GetActiveEnemiesOptimized()
{
    _activeEnemiesCache.Clear();
    foreach (var enemy in _allEnemies)
    {
        if (enemy.IsActive)
            _activeEnemiesCache.Add(enemy);
    }
    return _activeEnemiesCache;
}

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