自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C++中的 map vs unordered_map:選錯(cuò)容器讓你的程序慢十倍!

開發(fā)
今天我就帶大家徹底搞清楚map 和 unordered_map這兩個(gè)容器的區(qū)別,記住這些區(qū)別,下次寫代碼時(shí),就能輕松選擇正確的容器,讓你的程序飛起來(lái)!?

大家好,我是小康。

今天咱們聊一個(gè)看似簡(jiǎn)單卻經(jīng)常被忽視的話題:C++中的map和unordered_map到底有啥區(qū)別?

選錯(cuò)了容器,你的程序可能就慢了 10 倍不止!這可不是危言聳聽,而是實(shí)打?qū)嵉男阅懿罹唷?/p>

一、一個(gè)真實(shí)的"血淚"故事

前幾天我同事小王一臉沮喪地走過(guò)來(lái):"我的程序怎么這么慢啊,數(shù)據(jù)量一大就卡得不行..."

我瞄了一眼他的代碼,發(fā)現(xiàn)他在處理幾十萬(wàn)條數(shù)據(jù)時(shí)用的是map,而不是unordered_map。簡(jiǎn)單改了一下容器類型后,程序速度立馬提升了 8 倍多!

小王震驚了:"啥?就改個(gè)容器名字,速度差這么多?"

是的,就是這么神奇!今天我就帶大家徹底搞清楚這兩個(gè)容器的區(qū)別,以后再也不踩這個(gè)坑。

二、它們到底是啥?

1. map:有序的紳士

map就像一個(gè)有序的字典,它會(huì)自動(dòng)把你放進(jìn)去的鍵值對(duì)按鍵排序。

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> scoreMap;
    
    scoreMap["Zhang"] = 85;
    scoreMap["Li"] = 92;
    scoreMap["Wang"] = 78;
    
    // 遍歷時(shí)會(huì)按鍵的字母順序輸出
    for (constauto& student : scoreMap) {
        std::cout << student.first << ": " << student.second << std::endl;
    }
    
    return0;
}

// 輸出結(jié)果:
// Li: 92
// Wang: 78
// Zhang: 85
// (按照字母順序)

2. unordered_map:隨性的混世魔王

unordered_map則像個(gè)不講究順序的字典,它只關(guān)心能不能快速找到東西,至于排序?不存在的!

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<std::string, int> scoreMap;
    
    scoreMap["Zhang"] = 85;
    scoreMap["Li"] = 92;
    scoreMap["Wang"] = 78;
    
    // 遍歷時(shí)輸出順序不確定
    for (constauto& student : scoreMap) {
        std::cout << student.first << ": " << student.second << std::endl;
    }
    
    return0;
}

// 可能的輸出結(jié)果:
// Wang: 78
// Zhang: 85
// Li: 92
// (順序可能每次運(yùn)行都不同)

三、它們內(nèi)部是咋實(shí)現(xiàn)的?

1. map:紅黑樹(有規(guī)有矩的大家族)

map內(nèi)部是用 紅黑樹 實(shí)現(xiàn)的。紅黑樹是一種自平衡的二叉查找樹。

想象一下,如果把map比作一個(gè)圖書館:

  • 每本書(鍵值對(duì))都有固定的位置
  • 所有書按書名(鍵)字母順序排列
  • 要找一本書,圖書管理員會(huì)從中間的書架開始,然后告訴你"往左邊找"或"往右邊找"
  • 找書的過(guò)程就像二分查找
// map的簡(jiǎn)化結(jié)構(gòu)示意圖(紅黑樹)
          D
        /   \
       B     F
      / \   / \
     A   C E   G

在上面的圖中,每個(gè)字母代表一個(gè)鍵。查找鍵"E"的過(guò)程:

  • 從根節(jié)點(diǎn)"D"開始
  • "E"比"D"大,向右走到"F"
  • "E"比"F"小,向左走到"E"
  • 找到了!

2. unordered_map:哈希表(雜亂但高效的倉(cāng)庫(kù))

unordered_map內(nèi)部是用哈希表實(shí)現(xiàn)的。

繼續(xù)用圖書館打比方:

  • 這是一個(gè)特殊的圖書館,沒有明顯的排序
  • 但圖書管理員有一個(gè)神奇的公式,輸入書名就能直接告訴你書在哪個(gè)架子上
  • 你直接去那個(gè)架子就能找到書,不需要一步步查找
  • 這個(gè)"神奇公式"就是哈希函數(shù)
// unordered_map的簡(jiǎn)化結(jié)構(gòu)示意圖(哈希表)
桶0: [C] -> [K]
桶1: [A]
桶2: 
桶3: [D] -> [H]
桶4: [B]
桶5: [G]
桶6: [F] -> [J]
桶7: [E] -> [I]

在上圖中,每個(gè)字母代表一個(gè)鍵。查找鍵"H"的過(guò)程:

  • 計(jì)算"H"的哈希值,假設(shè)結(jié)果為 3
  • 直接檢查桶 3
  • 桶 3 有一個(gè)鏈表,檢查鏈表中的每個(gè)元素
  • 找到"H"!

四、性能對(duì)比:差距到底有多大?

讓我們做個(gè)全面的性能對(duì)比,分別測(cè)試插入、查找、刪除和修改這四種操作:

#include <iostream>
#include <map>
#include <unordered_map>
#include <chrono>
#include <string>
#include <vector>
#include <random>

// 計(jì)時(shí)輔助函數(shù)
template<typename Func>
long long timeOperation(Func func) {
    auto start = std::chrono::high_resolution_clock::now();
    func();
    auto end = std::chrono::high_resolution_clock::now();
    returnstd::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}

int main() {
    constint COUNT = 100000;
    
    // 準(zhǔn)備隨機(jī)數(shù)據(jù)
    std::vector<int> keys;
    for (int i = 0; i < COUNT; i++) {
        keys.push_back(i);
    }
    
    // 打亂順序用于隨機(jī)訪問
    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(keys.begin(), keys.end(), g);
    
    std::map<int, int> orderedMap;
    std::unordered_map<int, int> unorderedMap;
    
    // 1. 插入性能
    auto mapInsertTime = timeOperation([&]() {
        for (int i = 0; i < COUNT; i++) {
            orderedMap[i] = i * 2;
        }
    });
    
    auto unorderedMapInsertTime = timeOperation([&]() {
        for (int i = 0; i < COUNT; i++) {
            unorderedMap[i] = i * 2;
        }
    });
    
    // 2. 查找性能(順序訪問)
    auto mapLookupTime = timeOperation([&]() {
        int result = 0;
        for (int i = 0; i < COUNT; i++) {
            result += orderedMap[i];
        }
        // 防止編譯器優(yōu)化掉
        volatileint dummy = result;
    });
    
    auto unorderedMapLookupTime = timeOperation([&]() {
        int result = 0;
        for (int i = 0; i < COUNT; i++) {
            result += unorderedMap[i];
        }
        // 防止編譯器優(yōu)化掉
        volatileint dummy = result;
    });
    
    // 3. 查找性能(隨機(jī)訪問)
    auto mapRandomLookupTime = timeOperation([&]() {
        int result = 0;
        for (int key : keys) {
            result += orderedMap[key];
        }
        // 防止編譯器優(yōu)化掉
        volatileint dummy = result;
    });
    
    auto unorderedMapRandomLookupTime = timeOperation([&]() {
        int result = 0;
        for (int key : keys) {
            result += unorderedMap[key];
        }
        // 防止編譯器優(yōu)化掉
        volatileint dummy = result;
    });
    
    // 4. 修改性能
    auto mapUpdateTime = timeOperation([&]() {
        for (int i = 0; i < COUNT; i++) {
            orderedMap[i] = i * 3;
        }
    });
    
    auto unorderedMapUpdateTime = timeOperation([&]() {
        for (int i = 0; i < COUNT; i++) {
            unorderedMap[i] = i * 3;
        }
    });
    
    // 5. 刪除性能
    auto mapEraseTime = timeOperation([&]() {
        for (int key : keys) {
            if (key % 2 == 0) {  // 刪除一半的元素
                orderedMap.erase(key);
            }
        }
    });
    
    auto unorderedMapEraseTime = timeOperation([&]() {
        for (int key : keys) {
            if (key % 2 == 0) {  // 刪除一半的元素
                unorderedMap.erase(key);
            }
        }
    });
    
    // 打印結(jié)果
    std::cout << "操作\t\tmap(微秒)\tunordered_map(微秒)\t性能比" << std::endl;
    std::cout << "插入\t\t" << mapInsertTime << "\t\t" << unorderedMapInsertTime 
              << "\t\t\t" << (float)mapInsertTime / unorderedMapInsertTime << std::endl;
    
    std::cout << "順序查找\t" << mapLookupTime << "\t\t" << unorderedMapLookupTime 
              << "\t\t\t" << (float)mapLookupTime / unorderedMapLookupTime << std::endl;
    
    std::cout << "隨機(jī)查找\t" << mapRandomLookupTime << "\t\t" << unorderedMapRandomLookupTime 
              << "\t\t\t" << (float)mapRandomLookupTime / unorderedMapRandomLookupTime << std::endl;
    
    std::cout << "修改\t\t" << mapUpdateTime << "\t\t" << unorderedMapUpdateTime 
              << "\t\t\t" << (float)mapUpdateTime / unorderedMapUpdateTime << std::endl;
    
    std::cout << "刪除\t\t" << mapEraseTime << "\t\t" << unorderedMapEraseTime 
              << "\t\t\t" << (float)mapEraseTime / unorderedMapEraseTime << std::endl;
    
    return0;
}

// 輸出結(jié)果:
// 操作            map(微秒)       unordered_map(微秒)     性能比
// 插入            225419          116690                  1.93178
// 順序查找        103715          20122                   5.15431
// 隨機(jī)查找        127432          25890                   4.92205
// 修改            104918          20597                   5.09385
// 刪除            130040          29996                   4.33524

1. 性能分析

從測(cè)試結(jié)果可以清晰地看出:

(1) 插入操作:unordered_map比map快約1.9倍。這是因?yàn)閙ap每次插入都需要維護(hù)紅黑樹的平衡,而unordered_map只需計(jì)算哈希值并放入對(duì)應(yīng)的桶。

(2) 查找操作:

  • 順序查找:unordered_map比map快約5.2倍
  • 隨機(jī)查找:unordered_map比map快約4.9倍

查找是unordered_map最顯著的優(yōu)勢(shì),無(wú)論是順序還是隨機(jī)訪問模式下都有明顯提升。

  • 修改操作:unordered_map比map快約5.1倍。修改操作本質(zhì)上是先查找再賦值,所以性能差距與查找操作接近。
  • 刪除操作:unordered_map比map快約4.3倍。map刪除元素后可能需要重新平衡樹,而unordered_map只需從哈希表中刪除節(jié)點(diǎn)。

2. 小結(jié)

綜合來(lái)看,unordered_map在所有操作上都顯著優(yōu)于map,特別是在查找和修改操作上,性能提升達(dá)到了5倍左右。這意味著在大多數(shù)不需要有序遍歷的場(chǎng)景下,unordered_map是更優(yōu)的選擇。

記住這些差異,在實(shí)際開發(fā)中選擇合適的容器,可以為你的程序帶來(lái)顯著的性能提升。

五、什么時(shí)候用哪個(gè)?

1. 用 map 的情況

(1) 需要有序遍歷:如果你需要按鍵的順序遍歷元素

// 想按學(xué)生姓名字母順序打印成績(jī)單
std::map<std::string, int> scoreCard;
// ... 添加數(shù)據(jù) ...
for (const auto& item : scoreCard) {
    std::cout << item.first << ": " << item.second << std::endl;
}

(2) 需要范圍查詢:找出所有鍵在某個(gè)范圍內(nèi)的元素

// 查找所有名字在A到C之間的學(xué)生
auto start = scoreCard.lower_bound("A");
auto end = scoreCard.upper_bound("C");
for (auto it = start; it != end; ++it) {
    std::cout << it->first << ": " << it->second << std::endl;
}

(3) 需要穩(wěn)定的性能:最壞情況下查找復(fù)雜度是確定的O(log n)

2. 用 unordered_map 的情況

(1) 只關(guān)心查找速度:大多數(shù)情況下只是用來(lái)查找,不關(guān)心順序

// 快速查找某個(gè)學(xué)生的成績(jī)
std::unordered_map<std::string, int> scoreDB;
// ... 添加數(shù)據(jù) ...
std::cout << "Zhang's score: " << scoreDB["Zhang"] << std::endl;

(2) 數(shù)據(jù)量大:對(duì)于大數(shù)據(jù)量,unordered_map 的常數(shù)時(shí)間復(fù)雜度 O(1) 明顯優(yōu)于 map 的 O(log n)

(3) 不需要排序:如果不需要按鍵排序,就沒必要付出排序的成本

六、常見坑點(diǎn)

坑1:自定義類型做鍵

對(duì)于unordered_map,如果用自定義類型作為鍵,必須提供哈希函數(shù)和相等比較函數(shù):

struct Student {
    std::string name;
    int id;
    
    booloperator==(const Student& other) const {
        return id == other.id;
    }
};

// 為Student提供哈希函數(shù)
namespacestd {
    template<>
    struct hash<Student> {
        size_t operator()(const Student& s) const {
            return hash<int>()(s.id);
        }
    };
}

// 現(xiàn)在可以使用了
std::unordered_map<Student, int> studentScores;

坑2:性能波動(dòng)

unordered_map在某些情況下可能會(huì)遇到哈希沖突,導(dǎo)致性能下降。如果你的應(yīng)用對(duì)性能穩(wěn)定性要求高,可能需要考慮使用map。

坑3:內(nèi)存占用

unordered_map通常比map消耗更多內(nèi)存,因?yàn)楣1頌榱私档蜎_突概率,會(huì)預(yù)留一定的空間。

七、實(shí)際使用建議

  • 默認(rèn)選擇unordered_map:除非有特殊需求,一般情況下優(yōu)先使用unordered_map
  • 測(cè)試決定:對(duì)于性能關(guān)鍵的代碼,最好實(shí)際測(cè)試兩種容器的性能差異
  • 根據(jù)需求選擇:如果需要有序遍歷或范圍查詢,選擇map;如果只需要快速查找,選擇unordered_map
  • 考慮數(shù)據(jù)規(guī)模:數(shù)據(jù)量越大,兩者的性能差距可能越明顯

八、總結(jié)

選對(duì)容器,事半功倍;選錯(cuò)容器,徒增煩惱。

  • map:有序、穩(wěn)定、支持范圍查詢,但速度較慢(O(log n))
  • unordered_map:無(wú)序、速度快(O(1)),但內(nèi)存占用較大,且不支持范圍查詢

記住這些區(qū)別,下次寫代碼時(shí),就能輕松選擇正確的容器,讓你的程序飛起來(lái)!

責(zé)任編輯:趙寧寧 來(lái)源: 跟著小康學(xué)編程
相關(guān)推薦

2023-01-05 08:55:00

2022-10-27 07:09:34

DjangoAPIRedis

2023-06-13 13:52:00

Java 7線程池

2017-09-13 10:04:41

性能探究HashMap

2021-08-17 14:30:09

Windows電腦微軟

2022-12-12 08:35:51

Map容器接口

2025-03-03 13:12:33

C#代碼Python

2017-09-26 14:56:57

MongoDBLBS服務(wù)性能

2023-09-07 11:29:36

API開發(fā)

2021-06-02 22:54:34

技巧 Git Clone項(xiàng)目

2025-02-24 08:10:00

C#代碼開發(fā)

2023-01-03 13:30:14

C++代碼map

2016-07-07 15:38:07

京東

2018-09-27 15:42:15

Python編程語(yǔ)言技術(shù)

2011-07-20 09:11:58

C++

2024-06-12 12:28:23

2023-03-07 08:34:01

2020-09-16 16:07:34

Chrome插件瀏覽器

2024-12-06 06:20:00

代碼枚舉

2011-02-28 10:01:00

芯片有機(jī)塑料
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)