C ++의 map vs. hash_map


117

내가 가진 질문이 hash_mapmapC ++에 있습니다. mapSTL에 있지만 hash_map표준이 아님을 이해합니다 . 둘의 차이점은 무엇입니까?

답변:


133

그것들은 매우 다른 방식으로 구현됩니다.

hash_map( unordered_mapTR1 및 Boost에서는 대신 사용) 키가 테이블의 슬롯에 해시되고 값이 해당 키에 연결된 목록에 저장되는 해시 테이블을 사용합니다.

map 균형 이진 검색 트리 (일반적으로 빨강 / 검정 트리)로 구현됩니다.

unordered_map는 컬렉션의 알려진 요소에 액세스 하는 데 약간 더 나은 성능을 제공해야하지만 map추가 유용한 특성이 있습니다 (예 : 정렬 된 순서로 저장되어 처음부터 끝까지 순회 할 수 있음). unordered_map삽입 및 삭제가 map.


7
나는 공연에 대해 당신과 완전히 동의하지 않습니다. 성능은 여러 매개 변수의 영향을받으며 "더 빠르기 때문에"단 10 개의 항목에 대해 unorder_map을 사용하는 프로그래머를 꾸짖을 것입니다. 먼저 인터페이스 / 기능에 대해 걱정하고 나중에 성능에 대해 걱정하십시오.
Matthieu M.

24
네, 문제를 이해하면 도움이됩니다. 특정 규모까지는 세척 성능 측면에서 볼 수 있지만 데이터 볼륨이 커짐에 따라 서로 다른 방식으로 차이가 나는 두 컨테이너의 성능 특성을 이해하는 것이 중요합니다.
Joe

7
흥미롭게도, 무작위 조회를 많이하는 애플리케이션에서 std :: map을 boost :: unordered_map으로 교체했지만 맵의 모든 키를 반복합니다. 검색에 많은 시간을 절약했지만 반복을 통해 다시 얻었으므로 다시 맵으로 전환하여 애플리케이션 성능을 향상시킬 다른 방법을 찾고 있습니다.
Erik Garrison

4
@ErikGarrison 요소를 삽입하고 삭제하는 것보다 훨씬 더 많은 랜덤 액세스 및 반복을 사용하는 경우 개체를 트리와 hash_map 모두에 둘 수 있습니다 (포인터를 저장하거나 더 나은 방법은 shared_ptr을 실제 인스턴스를 사용하는 경우). 그러면 hash_map을 통한 O (1) 시간 액세스 시간과 맵을 통한 O (n) 반복 시간이 있습니다. 물론, 당신은 매번 포인터를 추가하고 제거하는 것을 기억해야합니다. 이 동작을 캡슐화하는 사용자 지정 컨테이너 클래스 (아마도 템플릿도 포함)를 쉽게 작성할 수 있습니다.
sprite

2
@ErikGarrison 물론이 방법을 시도하면 약간의 추가 공간을 지불하게됩니다. 그러나 포인터를 사용하기 때문에 너무 많으면 안됩니다. 정말로 원한다면, 당신은 배 밖으로 가서 AVL의 자체 구현을 작성하고 hash_map의 데이터 유형으로 노드 포인터를 사용할 수 있습니다. 그러면 트리의 노드에 O (1) 시간 액세스 할 수 있습니다. 필요할 때마다 선형 적으로 반복 할 수 있습니다. 물론 이것은 꽤 많은 코딩을 필요로 할 것이고 랜덤 액세스 위치에서 많은 것을 반복 할 필요가 없다면 그것이 효과가 있을지 확신하지 못합니다.
sprite

35

hash_map많은 라이브러리 구현에서 제공하는 공통 확장입니다. 이것이 바로 unordered_mapTR1의 일부로 C ++ 표준에 추가되었을 때로 이름이 변경된 이유 입니다. map은 일반적으로 red-black 트리와 같은 균형 잡힌 이진 트리로 구현됩니다 (구현은 물론 다양합니다). hash_map그리고 unordered_map일반적으로 해시 테이블로 구현됩니다. 따라서 순서가 유지되지 않습니다. unordered_map삽입 / 삭제 / 쿼리는 O (1) (일정 시간)이며 여기서 맵은 O (log n)입니다. 여기서 n은 데이터 구조의 항목 수입니다. 따라서 unordered_map더 빠르며 항목의 순서에 신경 쓰지 않으면 map. 때때로 당신은 질서를 유지하기를 원하며 (키로 정렬) 그것이 map선택이 될 것입니다.


9
나는 해시 맵이 충돌 가능성이있는 경우 O (N)의 최악의 액세스 권한이 있음을 지적 것 (나쁜 해시 FCN,로드 팩터가 너무 높은, 등)
KitsuneYMG

좋은 해시 맵은 예상 비용이 O (1)이지만 보장되지 않습니다. 잘못된 해시 맵은 O (1)가 아닌 예상 비용을 가질 수 있습니다.
선명

14

주요 차이점 중 일부는 복잡성 요구 사항에 있습니다.

  • A mapRed-Black Tree 데이터 구조 O(log(N))로 구현되므로 삽입 및 찾기 작업에 시간이 필요합니다 .

  • unordered_map의 '평균'시간이 필요 O(1)삽입 및 발견을위한, 그러나의 최악의 시간을 가질 수있다 O(N). Hash Table 데이터 구조를 사용하여 구현 되었기 때문 입니다.

따라서 일반적 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

기술적으로 모든 이진 검색 트리는 b- 트리 (1-2 트리)입니다. 즉, 빨강 / 검정 이외의 다른 것을 사용하는 STL은 모릅니다
KitsuneYMG

@ bk1e "적절한"B- 트리는 디스크 페이지와 잘 정렬되는 "풍부한"트리 노드를 원하는 데이터베이스에서 매우 유용합니다. OTOH, 이것은 "일반"프로그램에서 사용되는 "플랫"메모리 모델에서 그다지 유용하지 않습니다-내가 아는 모든 STL 구현은 레드-블랙 트리를 사용합니다.
Branko Dimitrijevic 2012

3

map의 모든 멤버 가 정렬되어 있으므로 balanced binary search tree(일반적으로 a rb_tree) 에서 구현 balanced binary search tree됩니다.

hash_maphashtable모든 구성원 hashtable이 정렬되지 않았으므로의 구성원이 정렬되지 않았으므로 hash_map(unordered_map).

hash_map은 C ++ 표준 라이브러리가 아니지만 이제 이름이 unordered_map( 이름이 바뀐 것으로 생각할 수 있음) C ++ 11 이후로 C ++ 표준 라이브러리가됩니다.이 질문을 참조하십시오. hash_map과 unordered_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_maphashtable구조는 다음과 같이 구현됩니다 .

여기에 이미지 설명 입력

아래 코드에서 나는의 주요 부분 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_treehash_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)의 차이점을 보여줍니다. 이미지는 How to choose between map and unordered_map? :

여기에 이미지 설명 입력


1

나는 무엇을 제공하는지 모르겠지만 hash_map은 150K의 부호없는 정수 키와 부동 값을 지우는 데 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.