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

С какими контейнерами работаешь чаще всего

1.0 Junior🔥 231 комментариев
#STL контейнеры и алгоритмы

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

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

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

С какими контейнерами работаешь чаще всего

В backend-разработке на C++, особенно при работе с высоконагруженными системами, я использую следующие контейнеры в зависимости от задачи:

1. std::vector — самый часто используемый контейнер

Когда использую: Почти всегда, когда нужна динамическая коллекция данных.

// Буферизация данных из сети
std::vector<char> network_buffer;
buffer.reserve(65536);  // Приделяю память заранее

// Batch-обработка запросов
std::vector<Request> batch;
batch.push_back(request1);
batch.push_back(request2);

// Работа с таблицами из БД
std::vector<UserRecord> users = db.query("SELECT * FROM users");

// Временные данные для сортировки
std::vector<Score> scores = get_leaderboard();
std::sort(scores.begin(), scores.end());

Преимущества:

  • Кэш-дружественность (continuous memory)
  • O(1) random access
  • Простые операции с памятью
  • Предсказуемая производительность

2. std::unordered_map — для быстрого поиска

Когда использую: Кэши, индексы, маппирование ID на данные, счётчики.

// Кэш пользователей по ID (исключает повторные запросы в БД)
std::unordered_map<uint64_t, User> user_cache;
auto it = user_cache.find(user_id);
if (it != user_cache.end()) {
    return it->second;  // O(1) в среднем
}

// Счётчик частоты слов
std::unordered_map<std::string, int> word_frequency;
for (const auto& word : text) {
    word_frequency[word]++;
}

// Маппирование сессий
std::unordered_map<std::string, SessionData> sessions;
sessions[session_token] = {user_id, timestamp, ip};

// Индекс для быстрого поиска
std::unordered_map<int, std::vector<int>> inverted_index;

Преимущества:

  • O(1) в среднем случае поиск
  • O(1) вставка/удаление
  • Не требует упорядоченности

Недостатки:

  • Худший случай O(n) при hash collisions
  • Не сохраняет порядок
  • Больше памяти чем std::map

3. std::map — для упорядоченного доступа

Когда использую: Когда нужен порядок, диапазонные запросы, сортировка.

// Кэш с временем жизни (нужна упорядоченность по времени)
std::map<int64_t, CacheEntry> timestamp_index;
auto old = timestamp_index.begin();
if (old->first < now - CACHE_TTL) {
    timestamp_index.erase(old);  // Удаляем самый старый
}

// Диапазонные запросы (все записи за период)
auto start = timestamp_map.lower_bound(start_time);
auto end = timestamp_map.upper_bound(end_time);
for (auto it = start; it != end; ++it) {
    process(it->second);
}

// Отсортированный индекс по цене
std::map<double, std::vector<Product>> price_index;

Преимущества:

  • O(log n) поиск, вставка, удаление
  • Упорядоченность гарантирована
  • Удобные диапазонные операции (lower_bound, upper_bound)

4. std::deque — для очередей и буферов с две концов

Когда использую: Очереди обработки, циклические буферы, скользящие окна.

// Очередь задач (добавляем в конец, берём с начала)
std::deque<Task> task_queue;
task_queue.push_back(new_task);      // O(1)
Task current = task_queue.front();
task_queue.pop_front();               // O(1)

// Скользящее окно (moving average)
std::deque<double> window;
for (double value : data_stream) {
    window.push_back(value);
    if (window.size() > WINDOW_SIZE) {
        window.pop_front();
    }
    double avg = calculate_average(window);
}

// Буфер с операциями с двух сторон
std::deque<Event> event_buffer;
event_buffer.push_back(new_event);    // Добавить в конец
event_buffer.push_front(urgent_event); // Добавить в начало

Преимущества:

  • O(1) операции с двумя концами
  • Кэш-дружественнее чем list
  • Хороша для очередей

5. std::set/std::multiset — для уникальности и упорядоченности

Когда использую: Уникальные ID, множество операций (объединение, пересечение), рейтинги.

// Множество активных сессий (для быстрого поиска)
std::set<SessionId> active_sessions;
if (active_sessions.count(session_id)) {
    // сессия активна
}

// Лидерборд (multiset с кастомным компаратором)
std::multiset<ScoreEntry> leaderboard;
leaderboard.insert({player_id, score});

// Множество заблокированных IP (для проверок)
std::set<std::string> blacklist;
if (!blacklist.count(client_ip)) {
    allow_connection();
}

6. std::list — редко, только когда нужны частые вставки/удаления в середине

Когда использую: Очень редко, в основном для структур данных которые требуютO(1) вставок в произвольном месте.

// Двусвязный список для LRU-кэша
std::list<CacheItem> cache_list;
std::unordered_map<Key, std::list<CacheItem>::iterator> index;

void access(Key key) {
    auto it = index[key];
    cache_list.erase(it);           // Удалить откуда угодно O(1)
    cache_list.push_front(*it);     // Добавить в начало O(1)
    index[key] = cache_list.begin();
}

Рекомендуемые комбинации для разных задач

Кэширование с вытеснением (LRU):

std::unordered_map<Key, std::list<Value>::iterator> index;
std::list<Value> recency_list;

Высоконагруженный бэкенд (обработка запросов):

std::deque<Request> request_queue;           // Входящие запросы
std::unordered_map<uint64_t, User> users;    // Кэш пользователей
std::map<int64_t, MetricEntry> metrics;      // Метрики по времени
std::vector<Response> batch_responses;       // Батч ответов

Аналитика и обработка данных:

std::vector<Record> raw_data;               // Сырые данные
std::unordered_map<std::string, int> freq;  // Подсчёты
std::map<double, std::vector<Item>> index;  // Индексы
std::set<Id> processed_ids;                 // Отслеживание обработанных

Практические советы по выбору

ЗадачаКонтейнерПричина
Буфер данныхvectorБыстро, кэш-friendly
Быстрый поиск по ключуunordered_mapO(1) avg
Диапазонные запросыmapsorted, O(log n)
Очередь (FIFO)dequeO(1) обе стороны
Уникальные значенияsetauto-dedupe, O(log n)
Частые вставки в серединуlistредко нужно!
Лидербордmultisetauto-sort, дубликаты

Общее правило: Используй vector по умолчанию, переходи на другие контейнеры только когда vector становится узким местом или семантика требует другого.