map
🔹 std::map trong C++ – 🚀
std::map
là một container trong C++ dùng để lưu trữ các cặp key-value (khóa - giá trị) theo thứ tự sắp xếp của khóa. Nó giống như một bảng tra cứu (lookup table) giúp tìm kiếm giá trị dựa trên khóa nhanh chóng.
📌 Đặc điểm của std::map
- ✅ Lưu trữ theo cặp (key -> value)
- ✅ Tự động sắp xếp key theo thứ tự tăng dần (theo operator<)
- ✅ Tìm kiếm, chèn, xóa phần tử có độ phức tạp O(log n) (dùng cây đỏ-đen)
- ✅ Không chứa key trùng lặp
- ✅ Cho phép truy cập nhanh theo key
Sử dụng
1. Khai báo và khởi tạo
#include <iostream>
#include <map>
#include <string>
int main() {
// Khai báo một map với khóa là int và giá trị là string
std::map<int, std::string> myMap;
// Khai báo và khởi tạo với danh sách khởi tạo (C++11 trở lên)
std::map<std::string, int> ages = {
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35}
};
// Khai báo và khởi tạo bằng cách sao chép từ một map khác
std::map<std::string, int> agesCopy = ages;
// Khai báo và khởi tạo bằng cách di chuyển từ một map khác
std::map<std::string, int> agesMoved = std::move(ages);
return 0;
}
2. Thêm phần tử vào map:
Có nhiều cách để thêm phần tử vào map:
- Sử dụng toán tử
[]
: Nếu khóa chưa tồn tại, một phần tử mới sẽ được tạo và giá trị sẽ được gán. Nếu khóa đã tồn tại, giá trị hiện tại sẽ bị ghi đè.
myMap[1] = "value1";
myMap[2] = "value2";
myMap[1] = "new value1"; // Ghi đè giá trị cho khóa 1
- Sử dụng phương thức
insert()
: Phương thức này trả về mộtstd::pair
. Phần tử đầu tiên của pair là một iterator trỏ đến phần tử đã được chèn (hoặc phần tử đã tồn tại). Phần tử thứ hai của pair là một giá trị boolean cho biết việc chèn có thành công hay không (true nếu chèn thành công, false nếu khóa đã tồn tại).
std::pair<int, std::string> pair1(3, "value3");
auto result1 = myMap.insert(pair1);
if (result1.second) {
std::cout << "Chèn thành công phần tử với khóa " << result1.first->first << std::endl;
} else {
std::cout << "Khóa " << result1.first->first << " đã tồn tại." << std::endl;
}
// Sử dụng make_pair
myMap.insert(std::make_pair(4, "value4"));
// Sử dụng initializer list (C++11 trở lên)
myMap.insert({5, "value5"});
3. Truy cập phần tử trong map:
- Sử dụng toán tử
[]
: Tương tự như khi thêm phần tử, nếu khóa tồn tại, nó sẽ trả về một tham chiếu đến giá trị tương ứng. Nếu khóa không tồn tại, nó sẽ tạo một phần tử mới với khóa đó và giá trị mặc định (được khởi tạo mặc định cho kiểu giá trị). Điều này có thể gây ra hiệu ứng phụ không mong muốn nếu bạn chỉ muốn kiểm tra sự tồn tại của khóa.
std::cout << "Giá trị của khóa 2: " << myMap[2] << std::endl;
std::cout << "Giá trị của khóa 6 (có thể tạo mới): " << myMap[6] << std::endl;
- Sử dụng phương thức
at()
: Phương thức này trả về một tham chiếu đến giá trị tương ứng với khóa. Nếu khóa không tồn tại, nó sẽ ném ra một ngoại lệstd::out_of_range
. Đây là cách an toàn hơn để truy cập phần tử khi bạn muốn đảm bảo khóa tồn tại.
try {
std::cout << "Giá trị của khóa 3: " << myMap.at(3) << std::endl;
std::cout << "Giá trị của khóa 7: " << myMap.at(7) << std::endl; // Sẽ ném ra ngoại lệ
} catch (const std::out_of_range& oor) {
std::cerr << "Lỗi: Khóa không tồn tại (" << oor.what() << ")" << std::endl;
}
- Sử dụng phương thức
find()
: Phương thức này tìm kiếm phần tử có khóa được chỉ định và trả về một iterator trỏ đến phần tử đó. Nếu không tìm thấy, nó trả về iteratorend()
.
auto it = myMap.find(4);
if (it != myMap.end()) {
std::cout << "Tìm thấy khóa " << it->first << ", giá trị: " << it->second << std::endl;
} else {
std::cout << "Không tìm thấy khóa 4." << std::endl;
}
4. Kiểm tra sự tồn tại của khóa:
- Sử dụng phương thức
count()
: Phương thức này trả về số lần khóa xuất hiện trong map. Vì các khóa trong map là duy nhất, nó sẽ trả về 1 nếu khóa tồn tại và 0 nếu không.
if (myMap.count(5)) {
std::cout << "Khóa 5 tồn tại trong map." << std::endl;
} else {
std::cout << "Khóa 5 không tồn tại trong map." << std::endl;
}
- Sử dụng phương thức
find()
.
5. Xóa phần tử khỏi map:
- Sử dụng phương thức
erase()
với khóa: Xóa phần tử có khóa được chỉ định. Trả về số lượng phần tử đã xóa (thường là 0 hoặc 1).
myMap.erase(2);
- Sử dụng phương thức
erase()
với iterator: Xóa phần tử mà iterator trỏ đến.
auto itToRemove = myMap.find(4);
if (itToRemove != myMap.end()) {
myMap.erase(itToRemove);
}
- Sử dụng phương thức
erase()
với phạm vi iterator: Xóa các phần tử trong phạm vi được chỉ định.
auto start = myMap.begin();
auto end = myMap.find(5);
if (end != myMap.end()) {
myMap.erase(start, std::next(end)); // Xóa từ đầu đến phần tử có khóa 5
}
- Sử dụng phương thức clear(): Xóa tất cả các phần tử khỏi map.
myMap.clear();
6. Duyệt qua các phần tử trong map:
- Sử dụng vòng lặp range-based for (C++11 trở lên): Đây là cách đơn giản và phổ biến nhất. Mỗi phần tử là một
std::pair
với first là khóa và second là giá trị.
for (const auto& pair : myMap) {
std::cout << "Khóa: " << pair.first << ", Giá trị: " << pair.second << std::endl;
}
- Sử dụng iterator:
for (std::map<int, std::string>::iterator it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << "Khóa: " << it->first << ", Giá trị: " << it->second << std::endl;
}
7. Các phương thức hữu ích khác:
size()
: Trả về số lượng phần tử trong map.empty()
: Trả về true nếu map rỗng, false nếu không.begin()
: Trả về một iterator trỏ đến phần tử đầu tiên (có khóa nhỏ nhất).end()
: Trả về một iterator trỏ đến vị trí sau phần tử cuối cùng.rbegin()
: Trả về một reverse iterator trỏ đến phần tử cuối cùng (có khóa lớn nhất).rend()
: Trả về một reverse iterator trỏ đến vị trí trước phần tử đầu tiên.lower_bound(key)
: Trả về một iterator trỏ đến phần tử đầu tiên có khóa không nhỏ hơn key.upper_bound(key)
: Trả về một iterator trỏ đến phần tử đầu tiên có khóa lớn hơn key.equal_range(key)
: Trả về một std::pair của iterators, đại diện cho phạm vi các phần tử có khóa bằng key. Vì khóa là duy nhất, phạm vi này sẽ chứa tối đa một phần tử.
All rights reserved