map和unordered_map區別及其優缺點
前言
C++的STL庫實現有兩種字典結構,即map和unordered_map(也就是通俗意義上的hash map)。這兩者雖然都稱為Map,但其實它們的底層實現原理具有很大差距,因此它們的使用場景也不盡相同。
今天特意研究了一下,下面從幾個方面具體談談它們的差別及其具體的使用場景。
介紹
字典類型又被稱為關聯數組(associative array),關聯數組和正常數組的使用方法是相似的,但其不同之處在於字典結構的下標不必是整數,而可以是任意類型。
map和unordered_map這兩種字典結構都是通過鍵值對(key-value)存儲數據的,鍵(key)和值(value)的數據類型可以不同。但是字典中的key只能存在一個,即必須唯一(如果不唯一,則被稱為multimap)。上述這點保證了值(value)可以直接通過鍵(key)來訪問,這便是字典結構最為便捷之處。
區別
1. 使用方法不同
使用方法是最直觀的區別,這兩種結構雖然都在STL庫中,但是所使用的頭文件不同。
- map:#include
- unordered_map:#include <unordered_map>
2. 底層實現的數據結構不同
數據結構其實是兩種類型最為根本的區別,其他的不同都是這種區別產生的結果。
- map是基於紅黑樹結構實現的。紅黑樹是一種平衡二叉查找樹的變體結構,它的左右子樹的高度差有可能會大於 1。所以紅黑樹不是嚴格意義上的平衡二叉樹AVL,但對之進行平衡的代價相對於AVL較低, 其平均統計性能要強於AVL。紅黑樹具有自動排序的功能,因此它使得map也具有按鍵(key)排序的功能,因此在map中的元素排列都是有序的。在map中,紅黑樹的每個節點就代表一個元素,因此實現對map的增刪改查,也就是相當於對紅黑樹的操作。對於這些操作的復雜度都為O(logn),復雜度即為紅黑樹的高度。
- unordered_map是基於哈希表(也叫散列表)實現的。散列表是根據關鍵碼值而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。散列表使得unordered_map的插入和查詢速度接近於O(1)(在沒有沖突的情況下),但是其內部元素的排列順序是無序的。
3. 元素排列順序不同
在2中已經解釋過了,現在單獨列出該點不同之處。
- map:基於紅黑樹,元素有序存儲
- unordered_map:基於散列表,元素無序存儲
4. 插入和查詢的時間復雜度不同
這點也已經在2中已經解釋過了,現在單獨列出該點不同。
- map:基於紅黑樹,復雜度與樹高相同,即O(logn)。
- unordered_map:基於散列表,復雜度依賴於散列函數產生的沖突多少,但大多數情況下其復雜度接近於O(1)。
5. 效率及其穩定性不同
這點實際上也是由底層的數據結構決定的。
- 存儲空間:unordered_map的散列空間會存在部分未被使用的位置,所以其內存效率不是100%的。而map的紅黑樹的內存效率接近於100%。
- 查找性能的穩定性:map的查找類似於平衡二叉樹的查找,其性能十分穩定。例如在1M數據中查找一個元素,需要多少次比較呢?20次。map的查找次數幾乎與存儲數據的分布與大小無關。而unordered_map依賴於散列表,如果哈希函數映射的關鍵碼出現的沖突過多,則最壞時間復雜度可以達到是O(n)。因此unordered_map的查找次數是與存儲數據的分布與大小有密切關系的,它的效率是不穩定的。
優缺點及適用場景
- map:
- 優點:
- map元素有序(這是map最大的優點,其元素的有序性在很多應用中都會簡化很多的操作);
- 其紅黑樹的結構使得map的很多操作都可在O(logn)下完成;
- map的各項性能較為穩定,與元素插入順序無關;
- map支持范圍查找。
- 缺點:
- 占用的空間大:紅黑樹的每一個節點需要保存其父節點位置、孩子節點位置及紅/黑性質,因此每一個節點占用空間大。
- 查詢平均時間不如unordered_map。
- 適用場景:
- 元素需要有序;
- 對於單次查詢時間較為敏感,必須保持查詢性能的穩定性,比如實時應用等等。
- 優點:
- unordered_map
- 優點:
- 查詢速度快,平均性能接近於常數時間O(1);
- 缺點:
- 元素無序;
- unordered_map相對於map空間占用更大,且其利用率不高;
- 查詢性能不太穩定,最壞時間復雜度可達到O(n)。
- 適用場景:
- 要求查找速率快,且對單次查詢性能要求不敏感。
- 優點:
結語
map和unordered_map並無好壞之分,它們都有各自應用的場景。它們之間的區別歸根結底來源於使用的數據結構不同。
最后一句話,總結一下它們的適用場景:
在需要元素有序性或者對單次查詢性能要求較為敏感時,請使用map,其余情況下應使用unordered_map。
因此在需要使用字典結構進行算法編程的大部分情況下,都需要使用unordered_map
而不是map
。
堅持不懈地努力才能成為大神!