C ++でのmapとhash_mapの比較


117

C ++ について質問がhash_mapありmapます。それmapはSTLにあることを理解していますhash_mapが、標準ではありません。2つの違いは何ですか?

回答:


133

それらは非常に異なる方法で実装されます。

hash_mapunordered_mapTR1とBoostでは、代わりにそれらを使用します)ハッシュテーブルを使用します。キーはテーブルのスロットにハッシュされ、値はそのキーに関連付けられたリストに格納されます。

map バランスのとれた二分探索木(通常は赤/黒木)として実装されます。

unordered_mapは、コレクションの既知の要素にアクセスするためのパフォーマンスが若干向上するはずmapですが、便利な特性が追加されます(たとえば、ソートされた順序で格納されるため、最初から最後まで走査できます)。 unordered_map挿入および削除の方がmap。より高速です。


7
パフォーマンスに関してはあなたに完全に同意しません。パフォーマンスはいくつかのパラメーターの影響を受け、「それはより速い」ので、私は、プログラマーがunordered_mapを10エントリーだけ使用して叱るでしょう。最初にインターフェース/機能を心配し、後でパフォーマンスを心配します。
Matthieu M.

24
ええ、はい、問題を理解すると役立ちます。特定の桁までは、それはおそらく洗浄のパフォーマンスですが、データ量が大きくなるにつれて異なる方法で逸脱するため、両方のコンテナーのパフォーマンス特性を理解することが重要です。
Joe

7
興味深いことに、アプリケーションでstd :: mapをboost :: unordered_mapと入れ替えただけで、ランダムなルックアップを多数実行するだけでなく、マップ内のすべてのキーを反復処理しています。ルックアップの時間を大幅に節約しましたが、繰り返しによってそれを取り戻したので、マップに切り替えて、アプリケーションのパフォーマンスを改善する他の方法を探しています。
Erik Garrison

4
@ErikGarrison要素の挿入と削除よりもランダムアクセスと反復をはるかに多く使用する場合、ツリーとhash_mapの両方にオブ​​ジェクトを含めることができます(ポインタを保存するか、さらにはshared_ptrを両方の同じオブジェクトに実際のインスタンスを使用していた場合)。次に、hash_mapを介したO(1)時間のアクセス時間と、マップを介したO(n)の反復時間を取得します。もちろん、常に両方からポインタを追加および削除することを忘れないでください。この動作をカプセル化するカスタムコンテナークラス(おそらくそれもテンプレート化する)を簡単に作成できます。
スプライト

2
@ErikGarrisonもちろん、この方法を試すと、わずかな追加スペースで支払うことになります。ただし、ポインタを使用するので、多すぎてはなりません。本当にやりたい場合は、やり過ぎてAVLの独自の実装を記述し、ノードポインターをhash_mapのデータ型として使用すると、O(1)時間でツリー内のノードにアクセスできます。必要な場所に線形に反復することができます。もちろん、これにはかなりの量のコーディングが必要になるため、ランダムアクセスの場所から繰り返しアクセスする必要がない限り、効果があるかどうかはわかりません。
スプライト

35

hash_map多くのライブラリ実装によって提供される共通の拡張機能でした。これがunordered_map、TR1の一部としてC ++標準に追加されたときに名前が変更された理由です。mapは通常、赤黒ツリーのようなバランスの取れたバイナリツリーで実装されます(実装はもちろん異なります)。 hash_mapそしてunordered_map一般的にハッシュテーブルで実装されています。したがって、順序は維持されません。 unordered_map挿入/削除/クエリはO(1)(一定時間)となり、マップはO(log n)となります。nはデータ構造内のアイテム数です。したがってunordered_map、より高速であり、アイテムの順序を気にしない場合は、を優先する必要がありますmap。順序を維持したい場合があります(キーで並べ替え)map。そのための選択です。


9
衝突が発生する可能性が高い場合(ハッシュfcnが悪い、負荷係数が高すぎるなど)、ハッシュマップが最悪の場合O(N)にアクセスすることを指摘します
KitsuneYMG

優れたハッシュマップにはO(1)の予想コストがあり、そうであるとは限りません。悪いハッシュマップはO(1)ではない予想コストを持っているかもしれません。
2014年

14

主な違いのいくつかは、複雑さの要件にあります。

  • A mapO(log(N))赤黒ツリーデータ構造として実装されているため、挿入および検索操作に時間がかかります。

  • アンはunordered_mapの「平均」の時間が必要ですO(1)挿入と発見のために、しかし、最悪のケースの時間を持つことが許されますO(N)。これは、ハッシュテーブルのデータ構造を使用して実装されているためです。

そのため、通常はunordered_mapより高速になりますが、格納するキーとハッシュ関数によっては、さらに悪化する可能性があります。


4

C ++仕様では、STLコンテナーに使用する必要があるアルゴリズムを正確に示していません。ただし、そのパフォーマンスには一定の制約がmapあり、他の連想コンテナ用のハッシュテーブルの使用は除外されます。(これらは最も一般的には赤/黒ツリーで実装されます。)これらの制約は、ハッシュテーブルが提供できるよりも、これらのコンテナーに対してより良い最悪の場合のパフォーマンスを必要とします。

ただし、多くの人は本当にハッシュテーブルを必要としています。そのため、ハッシュベースのSTL連想コンテナは、長年にわたって一般的な拡張機能でした。その結果、unordered_mapC ++標準の新しいバージョンに追加されました。


これは実際にはC ++ 0xではなくTR1(std :: tr1 :: unordered_map)に追加されました
Terry Mahaffey

map一般的にバランスのとれたbtree である理由は、operator<()位置を決定する手段としてを使用したためだと思いました。
KitsuneYMG 2010

@kts:STL実装は実際にBツリーを使用しますか?
bk1e 2010

技術的には、すべてのバイナリ検索ツリーはbツリー(1-2ツリー)です。そうは言っても、red / black以外のものを使用するSTLは知りません
KitsuneYMG

@ bk1e「適切な」Bツリーは、ディスクページとうまく整合する「太い」ツリーノードが必要なデータベースで非常に役立ちます。OTOH、これは「通常の」プログラムで使用される「フラット」なメモリモデルではあまり役に立ちません-私が知っているすべてのSTL実装は赤黒木を使用しています。
Branko Dimitrijevic

3

map内のすべてのメンバーがソートされ、マップも同様であるため、balanced binary search tree(通常はrb_tree)から実装されbalanced binary search treeます。

hash_mapから実装されていhashtableます。すべてのメンバーhashtableはソートされていないため、メンバーはソートされhash_map(unordered_map)ていません。

hash_mapはc ++標準ライブラリではありませんが、名前が変更されunordered_map(名前が変更されたと考えることができます)、c ++ 11がこの質問を参照するため、c ++標準ライブラリになります。hash_mapとunordered_mapの違いは?詳細については。

以下では、2つの型のマップがどのように実装されているかについてのソースコードからいくつかのコアインターフェイスを提供します。

地図:

以下のコードは、mapがのラッパーにbalanced binary search treeすぎないことを示していbalanced binary search treeます。ほぼすべての関数は、関数を呼び出すだけです。

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実施されhashtable、その構造多少このようなものです:

ここに画像の説明を入力してください

以下のコードでは、の主要部分を指定してhashtableから、を指定します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);

};

map'sだけのメンバーのようrb_treeに、hash_map's唯一のメンバーはhashtableです。以下のようなメインコードです:

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);
        };

};

以下の画像は、hash_mapに53のバケットがあり、いくつかの値を挿入する場合を示しています。これは内部構造です。

ここに画像の説明を入力してください

以下の画像は、mapとhash_map(unordered_map)の違いを示しています。

ここに画像の説明を入力してください


1

何が得られるのかわかりませんが、hash_mapは、150Kの符号なし整数キーと浮動小数点値をclear()するのに20秒以上かかります。私はただ実行して、他の誰かのコードを読んでいます。

これは、hash_mapを含める方法です。

#include "StdAfx.h"
#include <hash_map>

私はこれをここで読んだ https://bytes.com/topic/c/answers/570079-perfomance-clear-vs-swap

clear()はO(N)の次数であると言っています。それは私には非常に奇妙ですが、それはそうです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.