• STL 之 unordered_map


    原理

    unordered_map 内部实现是散列表,是一个无序的容器。内部实现的散列表采用了链地址法,意思是使用链表来解决散列冲突。当往容器中加入一个元素的时候,会计算散列值,然后取余之后放到一个桶 (bucket) 里。如果不断往容器加元素,那么所有的桶都会变成一个很长的链表,这样效率就很低了,这种情况应该如何避免呢?unordered_map 采用了扩容的机制,当负载因子 (load factor) 超过了阈值,就会进行一次扩容。负载因子的计算方法是总键值对数除以桶数。扩容的时候,会重新计算散列,重新放入到桶里。

    cplusplus 文档:https://www.cplusplus.com/reference/unordered_map/unordered_map/

    代码声明

    从以下的代码中,我们可以看到 unordered_map 内部的存储使用 allocator 来管理的,存储的是 std::pair 类型的数据。

      template<class _Key, class _Tp,
    	   class _Hash = hash<_Key>,
    	   class _Pred = std::equal_to<_Key>,
    	   class _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
        class unordered_map;
    

    自定义类型

    一个自定义的类型,如果要作为 Key,应该如何写代码呢?假如我们定义了一个 Apple 类型,里面只有构造器(构造器内部只有输出)。直接将 Apple 作为 key 会出现报以下错误。std::hash<Apple> does not provide a call provider. 这意味着需要我们自定义散列的规则。如果细细思考 unordered_map 的模板参数,你会发现你不仅需要定义散列的规则,还需要定义相等判定的规则。

    散列

    可以采用的实现方式:

    • 覆写 operator() 的类或者结构体,需要在 unordered_map 实例化的地方指定模板参数
    struct apple_hash {
      std::size_t operator()(const Apple& apple) const {
        return std::hash<int>()(apple.GetData());
      }
    };
    
    std::unordered_map<Apple, int, apple_hash> apple_to_int;
    
    • 特化 std::hash 来实现,经过特化后,在使用 unordered_map 的时候就不用指定模板参数了

    特化 std::hash 的方法如下,覆写 operator() 需要注意几个细节,返回类型是 size_t,输入的参数是 const 引用,函数体是 const。

    template<>
    struct std::hash<Apple> {
      std::size_t operator()(const Apple& apple) const {
        return std::hash<int>()(apple.GetData());
      }
    };
    

    判等

    • 覆写 operator() 的类或者结构体作为模板参数
    struct apple_equal {
      bool operator()(const Apple& x, const Apple& y) const {
        std::cout << "apple_equal" << std::endl;
        return x.GetData() == y.GetData();
      }
    };
    
    std::unordered_map<Apple, int, std::hash<Apple>, apple_equal> apple_to_int;  // 不知道怎么跳过第三个参数进行实例化hhh
    
    • 特化 std::equal_to来实现
    template<>
    struct std::equal_to<Apple> {
      bool operator()(const Apple& x, const Apple& y) const {
        return x.GetData() == y.GetData();
      }
    };
    
    • 覆写类的 operator==() 方法
    bool operator==(const Apple& apple) const {
      return this->data_ == apple.data_;
    }
    
  • 相关阅读:
    C#创建https请求并使用pfx证书 拓荒者
    "类型初始值设定项引发异常" 解决方法 拓荒者
    Web乱码解决方法 拓荒者
    Asp.net Ajax Accordion控件的用法 拓荒者
    【转】最强日期正则表达式 拓荒者
    【转】Http之Get/Post请求区别 拓荒者
    【转】Log4Net五步走 拓荒者
    SqlServer 错误:"SQL Server 无法生成 FRunCM 线程" 解决办法 拓荒者
    log4net用法实例 拓荒者
    Asp.net Ajax Calendar控件用法 拓荒者
  • 原文地址:https://www.cnblogs.com/zzk0/p/15574559.html
Copyright © 2020-2023  润新知