Словарь std::map

Последнее обновление: 27.03.2023

Карта или std::map представляет контейнер, где каждое значение ассоциировано с определенным ключом. И по этому ключу можно получить элемент. Причем ключи могут иметь только уникальные значения. Примером такого контейнера может служить словарь, где каждому слову сопоставляется его перевод или объяснение. Поэтому такие структуры еще называют словарями.

Стандартная библиотека C++ предоставляет два типа словарей: std::map<Key, Value> и std::unordered_map<Key, Value>. Эти типы представляют шаблоны, которые типизируются двумя типами. Первый тип - Key задает тип для ключей, а второй тип - Value устанавливает тип для значений.

Тип std::map определен в заголовочном файле <map>. Определение пустого словаря:

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products;
}

Здесь определен словарь products, который будет условно хранить цену товаров. Для ключей будет применяться тип std::string, а для значений - числа типа unsigned (условно в качестве ключа будет выступать название товара, а в качестве значения - его цена).

Обращение к элементам

Для обращения к элементам словаря - получения или изменения их значений, так же, как в массиве или векторе, применяется оператора индексирования []. Только вместо целочисленных индексов можно использовать ключи любого типа в следующем виде:

map[ключ]=значение

Например:

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products;
    // установка значений
    products["bread"] = 30;
    products["milk"] = 80;
    products["apple"] = 60;

    // получение значений
    std::cout << "bread\t" << products["bread"] << std::endl;
    std::cout << "milk\t" << products["milk"] << std::endl;
    std::cout << "apple\t" << products["apple"] << std::endl;
}

Здесь определен словарь products, в котором ключами служат строки, а значениями - числа типа unsigned. Поэтому для установки элемента в квадратные скобки передается ключ-строка, а присваивается значение-число:

products["bread"] = 30;

Будем считать, что ключ - название товара, а значение - цена товара. То есть в данном случае элементу с ключом "bread" присваивается значение 30. При этом не важно, что ранее создан пустой словарь, и в нем нет никакого элемента с ключом "bread" - если его нет, то он создается. Если же элемент с данным ключом уже есть, то меняется его значение.

Чтобы получить элемент по определенному ключу, используем тот же синтаксис. Например, поскольку значение элемента - число, то мы можем, обратившись по ключу, получить это число:

unsigned breadPrice = products["bread"];

В выше приведенной программе просто выводим значения элементов на консоль:

bread   30
milk    80
apple   60

Перебор элементов:

Для перебора элементов можно применять цикл for в стиле "for-each":

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products;
    // установка значений
    products["bread"] = 30;
    products["milk"] = 80;
    products["apple"] = 60;

    for (const auto& [product, price] : products)
        std::cout << product << "\t" << price << std::endl;
}

Рассмотрим определение цикла. Каждый элемент словаря фактически представляет объект типа std::pair<const Key, Value>, который хранит, как ключ, так и значение. В нашем случае это объект std::pair<const std::string, unsigned int>. И с помощью полей first и second данного объекта мы могли бы получить соответственно ключ и значение элемента:

for (const auto& element : products)
    std::cout << element.first << "\t" << element.second << std::endl;

Но начиная со стандарта С++17 также можно использовать другой синтаксис, который позволяет сразу разложить объект на отдельные части - ключ и значение:

for (const auto& [product, price] : products)
    std::cout << product << "\t" << price << std::endl;

В данном случае в product будет помещаться ключ, а в price - значение элемента словаря. В итоге при выполнении программы мы получим следующий консольный вывод:

apple   60
bread   30
milk    80

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

Инициализация элементов

Тот факт, что в словаре элементы представляют тип std::pair, позволяет инициализировать словарь объектами std::pair:

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products
    {
        std::pair<std::string, unsigned>{"bread", 30}, std::pair{"milk", 80}, std::pair{"apple", 60}
    };
}

И даже можно сократить определение:

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products
    {
        {"bread", 30}, {"milk", 80}, {"apple", 60}
    };
}

Удаление элементов

Как было показано выше, для добавления элемента в словарь достаточно просто установить для некоторого ключа какой-нибудь значение. Для удаления же элементов применяется функция erase(), в которую передается ключ удаляемого элемента:

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products
    {
        {"bread", 30}, {"milk", 80}, {"apple", 60}
    };
    
    products.erase("milk");   // удаляем элемент с ключом "milk"

    for (const auto& [product, price] : products)
        std::cout << product << "\t" << price << std::endl;
	// консольный вывод
	// apple   60
	// bread   30

}

Размер словаря

Для получения количества элементов в словаре применяется функция size(). Также класс map имеет функцию empty(), которая возвращает true, если словарь пуст.

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products
    {
        {"bread", 30}, {"milk", 80}, {"apple", 60}
    };

    std::cout << "Products count: " << products.size() << std::endl;	// Products count: 3
    std::cout << "Products is empty: " << std::boolalpha << products.empty() << std::endl; // Products is empty: false
}

Проверка наличия элемента

Чтобы проверить, есть ли в словаре элемент с определенным ключом, применяются функции count() (возвращает 1, если элемент есть, и 0 - если отсутствует) и contains() (возвращает true, если элемент есть, и false - если отсутствует). В обе функции передается ключ элемента:

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, unsigned> products
    {
        std::pair<std::string, unsigned>{"bread", 30}, std::pair{"milk", 80}, std::pair{"apple", 60}
    }; 
    std::cout << "Apple\t" << products.count("apple")<< std::endl;     // Apple   1
    std::cout << "Orange\t" << products.count("orange")<< std::endl;   // Orange  0

    std::cout << "Apple\t" << std::boolalpha << products.contains("apple")<< std::endl;     // Apple   true
    std::cout << "Orange\t" << std::boolalpha << products.contains("orange")<< std::endl;   // Orange  false
}

Неупорядоченные словари

Тип std::map определяет словарь, который упорядочиваниет все свои элементы - по умолчанию в порядке возрастания ключей. Если упорядоченность не нужна, можно применять ти std::unordered_map, который в целом предоставляет тот же самый функционал, только не упорядочивает элементы и определен в заголовочном файле <unordered_map>

#include <iostream>
#include <unordered_map>

int main()
{
    std::unordered_map<std::string, unsigned> products
    {
        std::pair<std::string, unsigned>{"bread", 30}, std::pair{"milk", 80}, std::pair{"apple", 60}
    };
    
    for (const auto& [product, price] : products)
        std::cout << product << "\t" << price << std::endl;
}

Консольный вывод:

apple   60
milk    80
bread   30

Итераторы

Стоит отметить, что итераторы типа std::map являеются константными, что не позволяет изменять значения элементов при переборе:

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, std::string> phoneBook 
    {
        {"+11111111111", "Tom"}, {"+12222222222", "Bob"}, {"+13333333333", "Sam"}
    };
    for(auto iter{phoneBook.begin()}; iter != phoneBook.end(); iter++)
    {
        std::cout << iter->first << "\t" << iter->second << std::endl;
    }
    // для получения итераторов также можно использовать функции cbegin и cend
    for(auto iter{phoneBook.cbegin()}; iter != phoneBook.cend(); iter++)
    {
        std::cout << iter->first << "\t" << iter->second << std::endl;
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850