C ++에서 HashMap을 사용하는 가장 좋은 방법은 무엇입니까?


174

STL에 HashMap API가 있다는 것을 알고 있지만 이에 관한 좋은 예제가있는 훌륭하고 철저한 문서를 찾을 수 없습니다.

좋은 예가 감사하겠습니다.


C ++ 1x hash_map 또는 std :: map에 대해 질문하고 있습니까?
philant

2
C ++의 java.util.HashMap과 같은 것을 원한다면 표준화 된 방법을 원합니다. 그렇지 않으면 최고의 비표준 라이브러리입니다. C ++ 개발자는 HashMap이 필요할 때 일반적으로 무엇을 사용합니까?
user855

답변:


238

표준 라이브러리에는 정렬 및 정렬되지 않은 맵 ( std::mapstd::unordered_map) 컨테이너가 포함됩니다. 정렬 된 맵에서 요소는 키를 기준으로 정렬되며 삽입 및 액세스는 O (log n)에 있습니다. 일반적으로 표준 라이브러리는 내부적 으로 순서가 지정된지도에 빨간색 검은 색 나무 를 사용 합니다. 그러나 이것은 구현 세부 사항 일뿐입니다. 순서가없는 맵에서 삽입 및 액세스는 O (1)에 있습니다. 해시 테이블의 또 다른 이름 일뿐입니다.

(순서)가있는 예 std::map:

#include <map>
#include <iostream>
#include <cassert>

int main(int argc, char **argv)
{
  std::map<std::string, int> m;
  m["hello"] = 23;
  // check if key is present
  if (m.find("world") != m.end())
    std::cout << "map contains key world!\n";
  // retrieve
  std::cout << m["hello"] << '\n';
  std::map<std::string, int>::iterator i = m.find("hello");
  assert(i != m.end());
  std::cout << "Key: " << i->first << " Value: " << i->second << '\n';
  return 0;
}

산출:

23
키 : hello 값 : 23

컨테이너에서 주문이 필요하고 O (log n) 런타임에 문제가 없다면을 사용하십시오 std::map.

그렇지 않으면 실제로 해시 테이블 (O (1) 삽입 / 액세스) std::unordered_map이 필요한 경우 std::mapAPI 와 비슷한 (예 : 위의 예에서는 검색하고 바꿔야 mapunordered_map) 체크 아웃하십시오 .

unordered_map용기가 도입 된 C ++ 11 표준 개정. 따라서 컴파일러에 따라 C ++ 11 기능을 활성화해야합니다 (예 : GCC 4.8을 사용 -std=c++11하는 경우 CXXFLAGS 에 추가 해야 함).

C ++ 11 릴리스 이전에도 unordered_map네임 스페이스에서 GCC가 지원 되었습니다 std::tr1. 따라서 이전 GCC 컴파일러의 경우 다음과 같이 사용하려고 시도 할 수 있습니다.

#include <tr1/unordered_map>

std::tr1::unordered_map<std::string, int> m;

또한 부스트의 일부입니다. 즉, 더 나은 이식성을 위해 해당 부스트 헤더 를 사용할 수 있습니다 .


1
하지만 표준 라이브러리는 해시 테이블 기반의 컨테이너가없는, 거의 모든 구현에는 어떤 형태로든에서 SGI의 STL에서. hash_map
James McNellis

HashMap 구현을 위해
unorder_map

2
@ShameelMohamed, 2017, 즉 C ++ 11 이후 6 년이 지나서 제공하지 않는 STL을 찾기 란 쉽지 않습니다 unordered_map. 따라서 비표준을 고려해야 할 이유가 없습니다 hash_map.
maxschlepzig

30

A hash_map는 표준화 목적으로 사용되는 것의 오래된 표준화되지 않은 버전입니다 unordered_map(원래 TR1에 있으며 C ++ 11 이후 표준에 포함됨). 이름에서 알 수 있듯이 std::map기본적으로 순서가 정렬되지 않은 것과 다릅니다. 예를 들어 from에서 begin()를 통해 반복하는 경우 end()1 별로 순서대로 항목을 가져 오지만 unordered_mapfrom에서 begin()를 통해 반복 end()하면 a에서 항목을 가져옵니다. 다소 임의의 순서.

unordered_map일반적으로 일정 복잡성을 미칠 것으로 예상된다. 즉, 삽입, 조회 등은 일반적으로 테이블에있는 항목 수에 관계없이 기본적으로 고정 된 시간이 걸립니다. 은 std::map아주 삽입하거나 항목이 성장 검색하는 시간을 의미하지만 - 항목의 수가 저장되는에 로그의 복잡성이 서서히 지도가 커짐을. 예를 들어, 백만 개의 항목 중 하나를 조회하는 데 1 마이크로 초가 걸리는 경우 2 백만 개의 항목 중 하나를 검색하는 데 약 2 마이크로 초, 4 백만 개의 항목 중 하나에 대해 3 마이크로 초, 8 백만의 하나에 대해 4 마이크로 초가 소요될 것으로 예상 할 수 있습니다. 아이템 등

실제적인 관점에서 볼 때 그것은 실제로 전체 이야기가 아닙니다. 본질적으로 간단한 해시 테이블의 크기는 고정되어 있습니다. 범용 컨테이너의 가변 크기 요구 사항에 맞추는 것은 쉽지 않습니다. 결과적으로 (잠재적으로) 테이블을 확장하는 작업 (예 : 삽입)은 잠재적으로 느리게 진행됩니다 (즉, 대부분 상당히 빠르지 만 주기적으로 느리게 진행됩니다). 테이블 크기를 변경할 수없는 조회는 일반적으로 훨씬 빠릅니다. 결과적으로 대부분의 해시 기반 테이블은 삽입 횟수에 비해 많은 조회를 수행 할 때 최상의 상태를 유지하는 경향이 있습니다. 많은 양의 데이터를 삽입하는 경우 테이블을 한 번 반복하여 결과를 검색합니다 (예 : 파일의 고유 단어 수 계산)std::map (아직 계산 복잡도는 다르므로 파일의 고유 단어 수에 따라 다름)


1std::less<T> 기본적으로 맵을 생성 할 때 순서는 세 번째 템플릿 매개 변수에 의해 정의됩니다 .


1
답변이 게시 된 지 9 년이 지났다는 것을 알고 있습니다.하지만 정렬되지 않은지도의 크기가 줄어들 수 있다는 사실을 언급하는 문서 링크가 있습니까? 일반적으로 std 콜렉션은 커집니다. 또한 많은 데이터를 삽입하지만 얼마나 많은 키를 삽입할지 미리 알고 있다면 생성시 맵의 크기를 지정할 수 있습니다. 이로 인해 기본적으로 크기 조정 비용이 무효화됩니다. .
Zonko

@ 존코 : 죄송합니다, 물어볼 때 이것을 눈치 채지 못했습니다. 내가 아는 한 unorder_map은 호출에 대한 응답을 제외하고 축소되지 않습니다 rehash. 를 호출 rehash하면 테이블 크기를 지정합니다. 해당 크기는 테이블에 대해 지정된 최대로드 계수를 초과하지 않는 한 사용됩니다 (이 경우로드 계수를 한계 내에 유지하기 위해 크기가 자동으로 증가 함).
Jerry 관

22

컴파일 오류를 생성하는 데 필요한 포함을 생략하지 않는보다 완전하고 유연한 예는 다음과 같습니다.

#include <iostream>
#include <unordered_map>

class Hashtable {
    std::unordered_map<const void *, const void *> htmap;

public:
    void put(const void *key, const void *value) {
            htmap[key] = value;
    }

    const void *get(const void *key) {
            return htmap[key];
    }

};

int main() {
    Hashtable ht;
    ht.put("Bob", "Dylan");
    int one = 1;
    ht.put("one", &one);
    std::cout << (char *)ht.get("Bob") << "; " << *(int *)ht.get("one");
}

포인터로 미리 정의되어 있지 않으면 키에 특히 유용하지는 않습니다. 일치하는 값은 그렇지 않습니다! 그러나 일반적으로 키에 문자열을 사용하므로 키 선언에서 "const void *"대신 "string"을 사용하면이 문제가 해결됩니다.


4
이 예제는 C ++에서 매우 나쁜 습관입니다. 강력한 형식의 언어를 사용하고을 사용하여 언어를 삭제했습니다 void*. 우선, unordered_map표준의 일부로 코드 를 포장하고 코드 유지 관리 성을 감소시킬 이유가 없습니다 . 그런 다음 포장을 고집하려면을 사용하십시오 templates. 그것이 바로 그들이하는 것입니다.
guyarad

강력하게 입력 했습니까? 당신은 아마 정적으로 입력 된 것을 의미합니다. 그가 const char ptr에서 void로 이동할 수 있다는 사실은 C ++을 정적으로 그러나 강력하게 유형화하지는 않습니다. 유형이 있지만 존재하지 않는 모호한 플래그를 활성화하지 않으면 컴파일러에서 아무 말도하지 않습니다.
Sahsahae '12

6

std::unordered_mapGCC stdlibc ++ 6.4에서 해시 맵 을 사용하는 증거

이것은 https://stackoverflow.com/a/3578247/895245 에서 언급 되었지만 다음 대답 에서는 C ++의 std :: map 안에 어떤 데이터 구조가 있습니까? GCC stdlibc ++ 6.4 구현에 대한 추가 증거를 다음과 같이 제공했습니다.

  • 클래스에 GDB 단계 디버깅
  • 성능 특성 분석

다음은 해당 답변에 설명 된 성능 특성 그래프의 미리보기입니다.

여기에 이미지 설명을 입력하십시오

사용자 정의 클래스 및 해시 함수를 사용하는 방법 unordered_map

이 대답은 그것을 해결합니다 : C ++ unorder_map 커스텀 클래스 유형을 키로 사용

발췌 : 평등 :

struct Key
{
  std::string first;
  std::string second;
  int         third;

  bool operator==(const Key &other) const
  { return (first == other.first
            && second == other.second
            && third == other.third);
  }
};

해시 기능 :

namespace std {

  template <>
  struct hash<Key>
  {
    std::size_t operator()(const Key& k) const
    {
      using std::size_t;
      using std::hash;
      using std::string;

      // Compute individual hash values for first,
      // second and third and combine them using XOR
      // and bit shifting:

      return ((hash<string>()(k.first)
               ^ (hash<string>()(k.second) << 1)) >> 1)
               ^ (hash<int>()(k.third) << 1);
    }
  };

}

0

표준 템플릿을 계속 사용하면서 클래스를 해시하는 방법을 찾으려는 사람들에게는 간단한 해결책이 있습니다.

  1. 클래스에서 동등 연산자 오버로드를 정의해야합니다 ==. 이 작업을 수행하는 방법을 모르는 경우 GeeksforGeeks에는 훌륭한 자습서가 있습니다 https://www.geeksforgeeks.org/operator-overloading-c/

  2. 표준 네임 스페이스에서 클래스 이름을 유형으로 해시라는 템플릿 구조체를 선언합니다 (아래 참조). XOR 및 비트 시프 팅을 사용하여 해시를 계산하는 예를 보여주는 훌륭한 블로그 게시물을 찾았지만이 질문의 범위를 벗어 났지만 해시 함수를 사용하는 방법에 대한 자세한 지침도 포함되어 있습니다 https://prateekvjoshi.com/ 2014 / 06 / 05 / c에서 사용자 정의 클래스를 사용하여 해시 함수 사용 /

namespace std {

  template<>
  struct hash<my_type> {
    size_t operator()(const my_type& k) {
      // Do your hash function here
      ...
    }
  };

}
  1. 그럼 새로운 해시 함수를 사용하여 해시 테이블을 구현하기 위해, 당신은 단지를 만들 필요가 std::map또는 std::unordered_map처럼 당신은 일반적으로 할 것이다 사용 my_type키로, 표준 라이브러리가 자동으로 해시에 당신이 (2 단계에서) 이전에 정의 된 해시 함수를 사용합니다 당신의 열쇠.
#include <unordered_map>

int main() {
  std::unordered_map<my_type, other_type> my_map;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.