Collections in C++
Comprehensive Overview of Collections in C++
The programming language C++ is flexible and strong, and it is well-known for these qualities. Effective data organisation and manipulation require the use of appropriate data structures when working with data. Collections are a crucial component of any programmer's toolkit in C++, and the Standard Template Library (STL) provides a large selection of container classes and collection management techniques.
STL Container Classes
The STL offers a number of container classes, each with its own special traits and use cases. These containers are templates that let you store a variety of data types, from fundamental types like characters and integers to user-defined types. Let's examine a few of the most popular C++ container classes:
1. Vector
A dynamic array that can change in size is the std::vector class. It is a fantastic option for situations where you require a collection with effective indexing since it provides quick random access to elements.
2. List
The doubly-linked list of the std::list class makes it easier to add or remove entries from any place in the list. Although index-accessing items is slower than with vectors, it allows constant-time insertion and deletion.
3. Deque
A dynamic array-like container that effectively permits adding or removing elements from both ends is the std::deque (short for "double-ended queue") class. When working with a collection that needs effective insertion and removal from the front and back, it's a versatile option.
4. Set
The std::set class represents a sorted associative container that stores distinct elements in a sorted manner. Duplicates are not permitted. Based on their values, you can quickly discover, add, or remove components.
5. Map
The key-value pair storage format of the std::map class uses keys to determine the order in which data is stored. You can use each key to correlate values with a particular key because they are all distinct.
6. Unordered Set/Map
The std::unordered_set and std::unordered_map classes are comparable to std::set and std::map, respectively, but they store data in hash tables. While this option speeds up lookup times on average, it does not ensure sorted ordering.
7. Stack
A stack data structure is implemented by the adapter class std::stack utilising one of the base containers, such as deque, list, or vector.
8. Queue
A different adapter class, the std::queue class, uses one of the base containers to construct a queue data structure. First-In-First-Out (FIFO) is the rule that is applied.
9. The Priority Queue
A priority queue is implemented by the adapter class std::priority_queue, utilising a vector as its base container. With the aid of priority queues, you are able to keep track of a group of components with specified priorities while ensuring that the element with the highest priority is always at the front.
Algorithms for collections
Along with container classes, a variety of other methods for manipulating containers are supported by the C++ STL. Using these approaches will drastically reduce the complexity of your code, making it simpler to read. The following are some popular algorithms:
1. std::sort
Vectors, lists, and arrays are just a few of the several types of containers that the std::sort algorithm can ascendingly order elements in.
2. std::find
If an element matching a specified criteria is found, the std::find algorithm finds it and returns an iterator to the first instance.
3. std::accumulate
The std::accumulate algorithm figures out the total (or sum) of the components in a container.
4. std::for_each
For each element in a container, the std::for_each algorithm applies a certain function.
5. std::transform
Each element in one container is subjected to the std::transform algorithm's application of a predetermined function, and the results are then stored in another container.
Examples of Collections in C++
Example1:
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
int main() {
// Declaring vector to store values
std::vector<int> numbers;
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
// Declaring map to store key-value pairs
std::map<std::string, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
ages["Charlie"] = 22;
// Declaring set to store unique values
std::set<int> uniqueNumbers;
uniqueNumbers.insert(10);
uniqueNumbers.insert(20);
uniqueNumbers.insert(30);
uniqueNumbers.insert(20); // This is ignored as it's a duplicate
// Declaring queue to manage a FIFO data structure
std::queue<std::string> tasks;
tasks.push("Task 1");
tasks.push("Task 2");
tasks.push("Task 3");
// Accessing and printing elements
std::cout << "Vector elements:";
for (int num: numbers) {
std::cout << " " << num;
}
std::cout << std::endl;
std::cout << "Map elements:" << std::endl;
for (const auto& pair : ages) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
std::cout << "Set elements:";
for (int num: uniqueNumbers) {
std::cout << " " << num;
}
std::cout << std::endl;
std::cout << "Queue elements:";
while (!tasks.empty()) {
std::cout << " " << tasks.front();
tasks.pop();
}
std::cout << std::endl;
return 0;
}
Output:
![Collections in C++](https://static.tutorialandexample.com/cpp/collections-in-cpp1.png)
Example2:
#include <iostream>
#include <stack>
#include <unordered_set>
#include <queue>
int main() {
// Declaring stack to store and process elements in a Last-In-First-Out (LIFO) order
std::stack<int> numbers;
numbers.push(10);
numbers.push(20);
numbers.push(30);
// Declaring an unordered set to store unique values
std::unordered_set<int> uniqueNumbers;
uniqueNumbers.insert(10);
uniqueNumbers.insert(20);
uniqueNumbers.insert(30);
uniqueNumbers.insert(20); // This is ignored as it's a duplicate
// Declaring priority queue to store elements in a priority order (max-heap by default)
std::priority_queue<int> priorityQueue;
priorityQueue.push(30);
priorityQueue.push(10);
priorityQueue.push(20);
// Accessing and printing elements from the stack
std::cout << "Stack elements (LIFO):";
while (!numbers.empty()) {
std::cout << " " << numbers.top();
numbers.pop();
}
std::cout << std::endl;
// Accessing and printing unique elements from the unordered set
std::cout << "Unique Set elements:";
for (const int& num : uniqueNumbers) {
std::cout << " " << num;
}
std::cout << std::endl;
// Accessing and printing elements from the priority queue (in priority order)
std::cout << "Priority Queue elements (max-heap):";
while (!priorityQueue.empty()) {
std::cout << " " << priorityQueue.top();
priorityQueue.pop();
}
std::cout << std::endl;
return 0;
}
Output:
![Collections in C++](https://static.tutorialandexample.com/cpp/collections-in-cpp2.png)
Choosing the Correct Container
Writing effective and maintainable code depends on choosing the right container for your particular activity. When making your decision, take into account the following factors:
Algorithmic Requirements: Consider the algorithms you intend to utilise with the container while determining the necessary algorithms. Some algorithms perform better when used with particular container types.
Access Pattern: Do you require quick random access or continuous insertions and deletions? Lists are effective for altering the collection's structure, whereas vectors offer quick indexing.
Duplicates: Do you need to permit duplicates in your collection? While other containers like vectors and lists permit duplicates, sets and maps require uniqueness.
Ordering: Do you require that the elements be stored in the correct order? Unordered containers lose their order, but sets, maps, and sorted vectors do.
Memory Overhead: The memory overhead of various containers varies. For instance, lists may use extra memory because of their linked structure, while vectors, on the other hand, have no cost.