1. 程式人生 > >unordered_map(hash_map)和map的比較

unordered_map(hash_map)和map的比較

bug 模仿 效果 哈希函數 info 不同 多余 const 可能

測試代碼:

#include <iostream>
using namespace std;
#include <string>
#include <windows.h>
#include <string.h>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <map>
const int maxval = 2000000 * 5;
#include <unordered_map>

void map_test()
{
    printf("map_test\n");
    map<int, int> mp;
    clock_t startTime, endTime;
    startTime = clock();
    for (int i = 0; i < maxval; i++)
    {
        mp[rand() % maxval]++;
    }
    endTime = clock();
    printf("%lf\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
    printf("insert finish\n");
    startTime = clock();
    for (int i = 0; i < maxval; i++)
    {
        if (mp.find(rand()%maxval) == mp.end())
        {
            //printf("not found\n");
        }
    }
    endTime = clock();
    printf("%lf\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
    printf("find finish\n");
    
    startTime = clock();
    for(auto it = mp.begin(); it!=mp.end(); it++)
    {
        
    }
    endTime = clock();
    printf("%lf\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
    printf("travel finish\n");
    
    printf("------------------------------------------------\n");
}

void hash_map_test()
{
    printf("hash_map_test\n");
    unordered_map<int, int> mp;
    clock_t startTime, endTime;
    startTime = clock();
    for (int i = 0; i < maxval; i++)
    {
        mp[rand() % maxval] ++;
    }
    endTime = clock();
    printf("%lf\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
    printf("insert finish\n");
    startTime = clock();
    for (int i = 0; i < maxval; i++)
    {
        if (mp.find(rand() % maxval) == mp.end())
        {
            //printf("not found\n");
        }
    }
    endTime = clock();
    printf("%lf\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
    printf("find finish\n");
    
    startTime = clock();
    for(auto it = mp.begin(); it!=mp.end(); it++)
    {
        
    }
    endTime = clock();
    printf("%lf\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
    printf("travel finish\n");
    
    printf("------------------------------------------------\n");
}

int main(int argc, char *argv[])
{
    srand(0);
    map_test();
    Sleep(1000);
    srand(0);
    hash_map_test();

    system("pause");
    return 0;
}

詳解:

map(使用紅黑樹)與unordered_map(hash_map)比較
????map理論插入、查詢時間復雜度O(logn)
????unordered_map理論插入、查詢時間復雜度O(1)


技術分享圖片

數據量較小時,可能是由於unordered_map(hash_map)初始大小較小,大小頻繁到達閾值,多次重建導致插入所用時間稍大。(類似vector的重建過程)。
哈希函數也是有消耗的(應該是常數時間),這時候用於哈希的消耗大於對紅黑樹查找的消耗(O(logn)),所以unordered_map的查找時間會多余對map的查找時間。


技術分享圖片

數據量較大時,重建次數減少,用於重建的開銷小,unordered_map O(1)的優勢開始顯現


技術分享圖片

技術分享圖片

數據量更大,優勢更明顯


使用空間:

技術分享圖片

技術分享圖片

前半部分為map,後半部分為unordered_map
unordered_map占用的空間比map略多,但可以接受。
map和unordered_map內部實現應該都是采用達到閾值翻倍開辟空間的機制(16、32、64、128、256、512、1024……)浪費一定的空間是不可避免的。並且在開雙倍空間時,若不能從當前開辟,會在其他位置開辟,開好後將數據移過去。數據的頻繁移動也會消耗一定的時間,在數據量較小時尤為明顯。


一種方法是手寫定長開散列。這樣做在數據量較小時有很好地效果(避免了數據頻繁移動,真正趨近O(1))。但由於是定長的,在數據量較大時,數據重疊嚴重,散列效果急劇下降,時間復雜度趨近O(n)。


一種折中的方法是自己手寫unordered_map(hash_map),將初始大小賦為一個較大的值。擴張可以模仿STL的雙倍擴張,也可以自己采用其他方法。這樣寫出來的是最優的,但是實現起來極為麻煩。
綜合利弊,我們組采用unordered_map。


附:使用Dev測試與VS2017測試效果相差極大???

技術分享圖片

技術分享圖片

效率差了10倍???
原因:
Dev

技術分享圖片

VS2017

技術分享圖片

在Debug下,要記錄斷點等調試信息,的確慢。
Release:不對源代碼進行調試,編譯時對應用程序的速度進行優化,使得程序在代碼大小和運行速度上都是最優的。
VS2017切到release後,還更快

技術分享圖片

除了前面說的Debug與release導致效率差異外,編譯器的不同也會導致效率差異。
學到了。

unordered_map(hash_map)和map的比較