Узнаем, какие бывают контейнеры в языке программирования C++ и что это вообще такое. Более подробно рассмотрим, что из себя представляют ассоциативные контейнеры map и set.
Что такое контейнеры в C++, для чего они нужны и какие бывают
В языке программирования C++ часто нужно работать с коллекциями объектов. Контейнеры как раз и представляют собой эти коллекции. Они хранят элементы определенного типа и позволяют совершать над ними различные операции.
Контейнеры можно использовать, например, чтобы хранить названия городов и их улиц в картографическом приложении. Или чтобы хранить плейлисты в приложении музыкального плеера.
В C++ контейнеры бывают двух видов: последовательные и ассоциативные. Последовательные контейнеры похожи на обычные массивы и бывают разных типов. Например:
- array — коллекция фиксированного размера. Не позволяет добавлять или удалять элементы;
- vector — имеет переменный размер. Можно добавлять и удалять элементы.
Ассоциативные контейнеры — это контейнеры, где с каждым элементом связан ключ доступа. В языке C++ ассоциативные контейнеры представлены картами (словарями) map и множествами set.
Контейнер map в C++
Контейнеры map хранят пары «ключ — значение». К каждому значению можно получить доступ по уникальному ключу. Такие контейнеры по-другому называются картами или словарями.
Как создать контейнер map
Создадим контейнер, который будет содержать названия стран в качестве ключей и их столиц в качестве значений.
Сначала подключаем заголовочные файлы <map> и <iostream>. Первый нужен для работы со словарем, а второй — для управления чтением и записью в стандартные потоки. В частности, он понадобится для вывода результатов в консоль. В самом начале исходника пишем:
#include <iostream> #include <map>
Заголовки подключены. Теперь объявляем пустой контейнер map. Делаем это в функции main, которая является точкой входа в приложение:
int main() {
std::map<std::string, std::string> capitals;
}
Мы создали контейнер map с именем capitals. Ключи и значения в нем будут иметь тип string, то есть являться строками. Теперь заполним этот контейнер:
capitals["Россия"] = "Москва"; capitals["Северная Корея"] = "Пхеньян"; capitals["Китай"] = "Пекин";
Как видим, чтобы установить необходимое значение, нужно использовать вот такой простой синтаксис:
map[ключ]=значение
Получение значений
Чтобы получить значение, нужно просто прописать в нужном месте:
map[ключ]
Далее получим значения по ключу и выведем результаты в консоль:
std::cout << "Россия\t" << capitals["Россия"] << std::endl; std::cout << "Северная Корея\t" << capitals["Северная Корея"] << std::endl; std::cout << "Китай\t" << capitals["Китай"] << std::endl;
Весь исходник программы будет выглядеть так:
#include <iostream>
#include <map>
int main() {
std::map<std::string, std::string> capitals;
capitals["Россия"] = "Москва";
capitals["Северная Корея"] = "Пхеньян";
capitals["Китай"] = "Пекин";
std::cout << "Россия\t" << capitals["Россия"] << std::endl;
std::cout << "Северная Корея\t" << capitals["Северная Корея"] << std::endl;
std::cout << "Китай\t" << capitals["Китай"] << std::endl;
}
После запуска этой программы в консоли получим следующее:
Россия Москва Северная Корея Пхеньян Китай Пекин
Еще один способ создания контейнера map
Так как элементы в словаре имеют тип pair, то можно проинициализировать их таким способом:
std::map<std::string, std::string> capitals {
std::pair<std::string, std::string>{"Россия", "Москва"}, std::pair{"Северная Корея", "Пхеньян"}, std::pair{"Китай", "Пекин"}
};
Есть возможность сократить инициализацию:
std::map<std::string, std::string> capitals {
{"Россия", "Москва"}, {"Северная Корея", "Пхеньян"}, {"Китай", "Пекин"}
};
Как видно, последний способ довольно лаконичный и позволяет быстро создать словарь.
Перебор элементов в контейнере map
В прошлом примере мы несколько раз использовали cout для вывода содержимого контейнера в консоль. Вместо этого можно использовать cout только один раз. Но для этого придется перебрать элементы контейнера при помощи цикла for. Сделать это можно так:
for (const auto& [country, capital] : capitals) std::cout << country << "\t" << capital << std::endl;
Или же есть возможность использовать поля first и second для элементов контейнера:
for (const auto& element : capitals) std::cout << element.first << "\t" << element.second << std::endl;
И в том и в другом случае вывод в консоли будет таким:
Китай Пекин Россия Москва Северная Корея Пхеньян
Следует обратить внимание на то, что порядок элементов изменился. Дело все в том, что элементы выводятся в консоль по возрастанию ключей, но поскольку ключи представляют собой строки, то они сортируются в алфавитном порядке.
Удаление элементов из контейнера map
Для удаления элементов из контейнера map существует функция erase. Вот так, например, можно удалить Китай:
capitals.erase("Китай");
Если проинициализировать словарь самым коротким способом, который был показан выше, и применить перебор элементов с помощью цикла for, то весь код вместе с удалением Китая будет выглядеть так:
#include <iostream>
#include <map>
int main() {
std::map<std::string, std::string> capitals {
{"Россия", "Москва"}, {"Северная Корея", "Пхеньян"}, {"Китай", "Пекин"}
};
capitals.erase("Китай");
for (const auto& element : capitals)
std::cout << element.first << "\t" << element.second << std::endl;
}
В консоли получим следующий вывод:
Россия Москва Северная Корея Пхеньян
Как видим, Китая в этом списке больше нет.
Проверка наличия элементов в контейнере map
Для проверки наличия элемента в контейнере map применяются функции count и contains. Функция count возвращает значение 1 в случае, если элемент присутствует в контейнере, и значение 0, если такого элемента нет. Проверим с помощью count наличие России и США в нашем контейнере capitals:
#include <iostream>
#include <map>
int main() {
std::map<std::string, std::string> capitals {
{"Россия", "Москва"}, {"Северная Корея", "Пхеньян"}, {"Китай", "Пекин"}
};
std::cout << "Россия\t" << capitals.count("Россия")<< std::endl;
std::cout << "США\t" << capitals.count("США")<< std::endl;
}
Вывод в консоли:
Россия 1 США 0
Функция contains возвращает true, если элемент имеется в контейнере, и false, если такой элемент не найден. Проверим наличие России и США теперь уже с помощью функции contains:
#include <iostream>
#include <map>
int main() {
std::map<std::string, std::string> capitals {
{"Россия", "Москва"}, {"Северная Корея", "Пхеньян"}, {"Китай", "Пекин"}
};
std::cout << "Россия\t" << std::boolalpha << capitals.contains("Россия")<< std::endl;
std::cout << "США\t" << std::boolalpha << capitals.contains("США")<< std::endl;
}
Вывод в консоли:
Россия true США false
Определение размера словаря в контейнере map
Для определения размера контейнера map в C++ есть функция size. Определим с ее помощью размер нашего словаря со странами и их столицами:
#include <iostream>
#include <map>
int main() {
std::map<std::string, std::string> capitals {
{"Россия", "Москва"}, {"Северная Корея", "Пхеньян"}, {"Китай", "Пекин"}
};
std::cout << "Размер словаря:\t" << capitals.size() << std::endl;
}
Вывод в консоли:
Размер словаря: 3
Еще есть функция empty, которая возвращает значение true в случае, если словарь пустой, и значение false, если нет. Применим ее к нашему словарю:
#include <iostream>
#include <map>
int main() {
std::map<std::string, std::string> capitals {
{"Россия", "Москва"}, {"Северная Корея", "Пхеньян"}, {"Китай", "Пекин"}
};
std::cout << "Словарь пустой:\t" << std::boolalpha << capitals.empty() << std::endl;
}
Понятно, что в нашем случае вывод будет таким:
Словарь пустой: false
Неупорядоченный контейнер map
Контейнер map упорядочивает свои элементы по возрастанию ключей. Если такая упорядоченность не нужна, то можно применить контейнер unordered_map. Перед использованием этого контейнера сначала нужно подключить соответствующий заголовочный файл, а потом уже делать все остальное:
#include <iostream>
#include <unordered_map>
int main() {
std::unordered_map<std::string, std::string> capitals {
{"Россия", "Москва"}, {"Северная Корея", "Пхеньян"}, {"Китай", "Пекин"}
};
for (const auto& element : capitals)
std::cout << element.first << "\t" << element.second << std::endl;
}
Получим следующий вывод в консоли:
Китай Пекин Северная Корея Пхеньян Россия Москва
Контейнер set в C++
Контейнеры set по-другому называются множествами. Это коллекции, которые хранят только уникальные элементы, то есть не могут содержать в себе дубликаты. Каждое значение элемента также является и ключом.
Как создать контейнер set
Для создания такого контейнера сначала нужно подключить заголовочный файл <set>:
#include <set>
Нам понадобится также заголовок <iostream>:
#include <iostream>
Далее в функции main создаем множество. Пусть это будет пока еще пустое множество букв латинского алфавита:
std::set<std::string> letters;
Теперь инициализируем множество пятью первыми буквами:
std::set<std::string> letters{"a", "b", "c", "d", "e"};
Перебор элементов и вывод в консоль
Чтобы перебрать все элементы контейнера set и вывести их в консоль, очень удобно использовать цикл for:
for (std::string letter : letters) std::cout << letter << "\t"; std::cout << std::endl;
Последняя строчка нужна для перехода на новую строку. Весь исходник будет выглядеть следующим образом:
#include <iostream>
#include <set>
int main() {
std::set<std::string> letters{"a", "b", "c", "d", "e"};
for (std::string letter : letters)
std::cout << letter << "\t";
std::cout << std::endl;
}
Вывод в консоли:
a b c d e
Добавление элементов в контейнер set
Для добавления элементов в контейнер set служит функция insert. Добавим еще две буквы в наше множество letters:
#include <iostream>
#include <set>
int main() {
std::set<std::string> letters{"a", "b", "c", "d", "e"};
letters.insert("f");
letters.insert("g");
for (std::string letter : letters)
std::cout << letter << "\t";
std::cout << std::endl;
}
Получим такой вывод:
a b c d e f g
Как уже говорилось в начале этого раздела, в контейнере set могут быть только уникальные элементы. Поэтому, если мы сделаем, например, вот так:
letters.insert("f");
letters.insert("g");
letters.insert("g");
letters.insert("g");
То есть попытаемся добавить букву «g» три раза, то все равно получим в консоли вывод из семи букв, как в прошлом примере.
Удаление элементов из контейнера set
Во множествах, как и в словарях, для удаления элементов применяется функция erase. Удалим, например, букву «c» из нашего множества letters:
#include <iostream>
#include <set>
int main() {
std::set<std::string> letters{"a", "b", "c", "d", "e"};
letters.erase("c");
for (std::string letter : letters)
std::cout << letter << "\t";
std::cout << std::endl;
}
Вывод в консоли:
a b d e
Как видим, элемент успешно удален.
Проверка наличия элементов в контейнере set
Проверить наличие элементов в контейнере set можно при помощи функций count и contains. Вот пример проверки с помощью count:
#include <iostream>
#include <set>
int main() {
std::set<std::string> letters{"a", "b", "c", "d", "e"};
std::cout << "Элемент d\t" << letters.count("d") << std::endl;
std::cout << "Элемент j\t" << letters.count("j") << std::endl;
}
Вывод в консоли:
Элемент d 1 Элемент j 0
Пример проверки функцией contains:
#include <iostream>
#include <set>
int main() {
std::set<std::string> letters{"a", "b", "c", "d", "e"};
std::cout << "Элемент d\t" << std::boolalpha << letters.contains("d") << std::endl;
std::cout << "Элемент j\t" << std::boolalpha << letters.contains("j") << std::endl;
}
В консоли получим:
Элемент d true Элемент j false
Все работает так же, как и со словарями.
Определение размера множества
И здесь тоже применяются те же функции empty и size, которые мы рассматривали в разделе про словари. И применяются они точно так же:
#include <iostream>
#include <set>
int main() {
std::set<std::string> letters{"a", "b", "c", "d", "e"};
std::cout << "Пустое множество: " << std::boolalpha << letters.empty() << std::endl;
std::cout << "Размер множества: " << letters.size() << std::endl;
}
В консоли будет следующий вывод:
Пустое множество: false Размер множества: 5
Неупорядоченный контейнер set
Контейнер set упорядочивает свои элементы. Если упорядочивание элементов не требуется, то вместо set можно использовать контейнер unordered_set. Этот контейнер практически ничем не отличается от set, кроме того, что содержит неупорядоченные элементы. Пример:
#include <iostream>
#include <unordered_set>
int main() {
std::unordered_set<std::string> letters{"a", "b", "c", "d", "e"};
for (std::string letter : letters)
std::cout << letter << "\t";
std::cout << std::endl;
}
Вывод в консоли:
e c b d a
Далее предлагаем вам решить несложную задачу.
Задание для самостоятельного выполнения
Дана следующая таблица:
| Преступление и наказание | Ф.Достоевский |
| Горе от ума | А.Грибоедов |
| Евгений Онегин | А.Пушкин |
| Мертвые души | Н.Гоголь |
| Война и мир | Л.Толстой |
В левой колонке содержатся ключи, а в правой — связанные с ними значения. Требуется создать контейнер map из этих данных и вывести его содержимое в консоль. Далее из ключей создайте контейнер set и с помощью соответствующей функции добавьте в него книги «Анна Каренина» и «Вишневый сад». Содержимое контейнера также нужно вывести в консоль.
Решение
Для контейнера map код будет таким:
#include <iostream>
#include <map>
int main()
{
std::map<std::string, std::string> writers
{
{"Преступление и наказание", "Ф.Достоевский"}, {"Горе от ума", "А.Грибоедов"}, {"Евгений Онегин", "А.Пушкин"}, {"Мертвые души", "Н.Гоголь"}, {"Война и мир", "А.Толстой"}
};
for (const auto& element : writers)
std::cout << element.first << "\t" << element.second << std::endl;
}
Вывод в консоли:
Война и мир А.Толстой Горе от ума А.Грибоедов Евгений Онегин А.Пушкин Мертвые души Н.Гоголь Преступление и наказание Ф.Достоевский
Для контейнера set код будет такой:
#include <iostream>
#include <set>
int main() {
std::set<std::string> books{"Преступление и наказание", "Горе от ума", "Евгений Онегин", "Мертвые души", "Война и мир"};
books.insert("Анна Каренина");
books.insert("Вишневый сад");
for (std::string book : books)
std::cout << book << "\t";
std::cout << std::endl;
}
Вывод в консоли:
Анна Каренина Вишневый сад Война и мир Горе от ума Евгений Онегин Мертвые души Преступление и наказание
