Câu trả lời:
Chúng được thực hiện theo những cách rất khác nhau.
hash_map
( unordered_map
trong TR1 và Boost; sử dụng chúng thay thế) sử dụng bảng băm trong đó khóa được băm vào một vị trí trong bảng và giá trị được lưu trữ trong danh sách gắn với khóa đó.
map
được thực hiện dưới dạng cây tìm kiếm nhị phân cân bằng (thường là cây đỏ / đen).
An unordered_map
sẽ cho hiệu suất tốt hơn một chút để truy cập các phần tử đã biết của tập hợp, nhưng a map
sẽ có các đặc điểm hữu ích bổ sung (ví dụ: nó được lưu trữ theo thứ tự được sắp xếp, cho phép duyệt từ đầu đến cuối). unordered_map
sẽ nhanh hơn khi chèn và xóa so với a map
.
hash_map
là một phần mở rộng phổ biến được cung cấp bởi nhiều triển khai thư viện. Đó chính xác là lý do tại sao nó được đổi tên thành unordered_map
khi nó được thêm vào tiêu chuẩn C ++ như một phần của TR1. bản đồ thường được thực hiện với một cây nhị phân cân bằng giống như một cây đỏ đen (tất nhiên là các cách triển khai khác nhau). hash_map
và unordered_map
thường được triển khai với bảng băm. Do đó trật tự không được duy trì. unordered_map
insert / delete / query sẽ là O (1) (thời gian không đổi) trong đó map sẽ là O (log n) với n là số lượng mục trong cấu trúc dữ liệu. Như vậy unordered_map
là nhanh hơn, và nếu bạn không quan tâm đến thứ tự của các mặt hàng nên được ưu tiên hơn map
. Đôi khi bạn muốn duy trì trật tự (sắp xếp theo khóa) và đó map
sẽ là sự lựa chọn.
Một số khác biệt chính là yêu cầu về độ phức tạp.
A map
yêu cầu O(log(N))
thời gian cho các thao tác chèn và tìm, vì nó được triển khai dưới dạng cấu trúc dữ liệu Cây Đỏ-Đen .
An unordered_map
yêu cầu thời gian 'trung bình' O(1)
cho các lần chèn và tìm, nhưng được phép có thời gian trong trường hợp xấu nhất là O(N)
. Điều này là do nó được triển khai bằng cấu trúc dữ liệu Bảng băm .
Vì vậy, thông thường, unordered_map
sẽ nhanh hơn, nhưng tùy thuộc vào các phím và hàm băm bạn lưu trữ, có thể trở nên tồi tệ hơn nhiều.
Thông số C ++ không nói chính xác thuật toán bạn phải sử dụng cho các vùng chứa STL. Tuy nhiên, nó đặt ra một số ràng buộc nhất định đối với hiệu suất của chúng, điều này loại trừ việc sử dụng bảng băm map
và các vùng chứa kết hợp khác. (Chúng thường được triển khai nhất với cây đỏ / đen.) Những ràng buộc này yêu cầu hiệu suất trong trường hợp xấu nhất cho các vùng chứa này tốt hơn so với bảng băm có thể cung cấp.
Tuy nhiên, nhiều người thực sự muốn có bảng băm, vì vậy các thùng chứa liên kết STL dựa trên băm đã là một phần mở rộng phổ biến trong nhiều năm. Do đó, họ đã thêm unordered_map
và như vậy vào các phiên bản sau của tiêu chuẩn C ++.
map
nói chung là một ngôi nhà cân bằng là do việc sử dụng operator<()
làm phương tiện xác định vị trí.
map
được thực hiện từ balanced binary search tree
(thường là a rb_tree
), vì tất cả các thành viên trong balanced binary search tree
đều được sắp xếp nên bản đồ cũng vậy;
hash_map
được triển khai từ hashtable
. Vì tất cả thành viên trong hashtable
không được sắp xếp nên các thành viên trong hash_map(unordered_map)
không được sắp xếp.
hash_map
không phải là thư viện chuẩn c ++, nhưng bây giờ nó được đổi tên thành unordered_map
(bạn có thể nghĩ nó đã được đổi tên) và trở thành thư viện chuẩn c ++ kể từ c ++ 11, hãy xem câu hỏi này Sự khác biệt giữa hash_map và unardered_map? để biết thêm chi tiết.
Dưới đây tôi sẽ đưa ra một số giao diện cốt lõi từ mã nguồn về cách thực hiện hai loại bản đồ.
Đoạn mã dưới đây chỉ để cho thấy rằng, bản đồ chỉ là một trình bao bọc của một balanced binary search tree
, hầu như tất cả những gì hàm của nó chỉ là gọi balanced binary search tree
hàm.
template <typename Key, typename Value, class Compare = std::less<Key>>
class map{
// used for rb_tree to sort
typedef Key key_type;
// rb_tree node value
typedef std::pair<key_type, value_type> value_type;
typedef Compare key_compare;
// as to map, Key is used for sort, Value used for store value
typedef rb_tree<key_type, value_type, key_compare> rep_type;
// the only member value of map (it's rb_tree)
rep_type t;
};
// one construct function
template<typename InputIterator>
map(InputIterator first, InputIterator last):t(Compare()){
// use rb_tree to insert value(just insert unique value)
t.insert_unique(first, last);
}
// insert function, just use tb_tree insert_unique function
//and only insert unique value
//rb_tree insertion time is : log(n)+rebalance
// so map's insertion time is also : log(n)+rebalance
typedef typename rep_type::const_iterator iterator;
std::pair<iterator, bool> insert(const value_type& v){
return t.insert_unique(v);
};
hash_map
:hash_map
được triển khai từ hashtable
có cấu trúc giống như sau:
Trong đoạn mã dưới đây, tôi sẽ đưa ra phần chính của hashtable
, và sau đó đưa ra hash_map
.
// used for node list
template<typename T>
struct __hashtable_node{
T val;
__hashtable_node* next;
};
template<typename Key, typename Value, typename HashFun>
class hashtable{
public:
typedef size_t size_type;
typedef HashFun hasher;
typedef Value value_type;
typedef Key key_type;
public:
typedef __hashtable_node<value_type> node;
// member data is buckets array(node* array)
std::vector<node*> buckets;
size_type num_elements;
public:
// insert only unique value
std::pair<iterator, bool> insert_unique(const value_type& obj);
};
Giống như map's
thành viên duy nhất là rb_tree
, hash_map's
thành viên duy nhất là hashtable
. Mã chính của nó như sau:
template<typename Key, typename Value, class HashFun = std::hash<Key>>
class hash_map{
private:
typedef hashtable<Key, Value, HashFun> ht;
// member data is hash_table
ht rep;
public:
// 100 buckets by default
// it may not be 100(in this just for simplify)
hash_map():rep(100){};
// like the above map's insert function just invoke rb_tree unique function
// hash_map, insert function just invoke hashtable's unique insert function
std::pair<iterator, bool> insert(const Value& v){
return t.insert_unique(v);
};
};
Hình ảnh dưới đây cho thấy khi một hash_map có 53 nhóm và chèn một số giá trị, đó là cấu trúc bên trong.
Hình ảnh dưới đây cho thấy một số khác biệt giữa bản đồ và hash_map (bản đồ không có thứ tự), hình ảnh đến từ Cách chọn giữa bản đồ và bản đồ không có thứ tự? :
Tôi không biết điều gì mang lại, nhưng, hash_map mất hơn 20 giây để xóa () 150K khóa số nguyên không dấu và giá trị float. Tôi chỉ đang chạy và đọc mã của người khác.
Đây là cách nó bao gồm hash_map.
#include "StdAfx.h"
#include <hash_map>
Tôi đọc cái này ở đây https://bytes.com/topic/c/answers/570079-perfomance-clear-vs-swap
nói rằng clear () là bậc của O (N). Điều đó đối với tôi, rất lạ, nhưng, đó là cách của nó.