【问题标题】:How can I use a C++ unordered_set for a custom class?如何将 C++ unordered_set 用于自定义类?
【发布时间】:2016-11-27 23:43:51
【问题描述】:

如何将类的对象存储在unordered_set 中?我的程序需要经常检查 unordered_set 中是否存在对象,如果存在,则对该对象进行一些更新。

我在网上查找了如何使用unordered_set,但遗憾的是大多数教程都是关于在intstring 类型上使用它。但是我怎样才能在课堂上使用它呢?如何定义哈希函数以使以下示例中的node_id 成为unordered_set 的键?

#include <iostream>
#include <unordered_set>

using namespace std;

// How can I define a hash function that makes 'node' use 'node_id' as key?    
struct node
{
    string node_id;
    double value;
    node(string id, double val) : node_id(id), value(val) {}
};

int main()
{
    unordered_set<node> set;
    set.insert(node("1001", 100));
    if(set.find("1001") != set.end()) cout << "1001 found" << endl;
}

【问题讨论】:

  • 看起来你需要一张地图而不是一组。
  • 我实际上是在使用地图。但它读到无序集的查找复杂度为 O(1)
  • @daydayup unordered_map 也是如此。
  • 一个警告:您说您正在查找对象以更改它。如果此更改影响密钥,您将需要删除旧密钥并重新插入新密钥。

标签: c++ unordered-set hash-function


【解决方案1】:

您可以尝试使用以下哈希函数对象(它非常基本,因此您可能需要对其进行改进以避免太多冲突)。

struct node_hash {
    std::size_t operator()(const node& _node) const {
        return std::hash<std::string>()(_node.node_id);
    }
}
// ...
std::unordered_set<node, node_hash> node_set;

但是,正如其中一位 cmets 指出的那样,您最好在此处使用 std::unordered_map&lt;std::string, double&gt;

【讨论】:

    【解决方案2】:

    由于这是 C++ unordered_set of objects 在 Stack Overflow 上的最高 Google 结果,我将发布一个简单但完全说明性并复制/粘贴可运行的示例:

    // UnorderedSetOfObjects.cpp
    
    #include <iostream>
    #include <vector>
    #include <unordered_set>
    
    struct Point
    {
      int x;
      int y;
    
      Point() { }
      Point(int x, int y)
      {
        this->x = x;
        this->y = y;
      }
      
      bool operator==(const Point& otherPoint) const
      {
        if (this->x == otherPoint.x && this->y == otherPoint.y) return true;
        else return false;
      }
    
      struct HashFunction
      {
        size_t operator()(const Point& point) const
        {
          size_t xHash = std::hash<int>()(point.x);
          size_t yHash = std::hash<int>()(point.y) << 1;
          return xHash ^ yHash;
        }
      };
    };
    
    int main(void)
    {
      std::unordered_set<Point, Point::HashFunction> points;
    
      points.insert(Point(1, 1));
      points.insert(Point(2, 2));
      points.insert(Point(1, 1));   // notice this is a duplicate with the 1st point so it won't change the set
    
      std::cout << "points: " << "\n";
      for (auto& point : points)
      {
        std::cout << "(" << point.x << ", " << point.y << ")" << "\n";
      }
    
      return 0;
    }
    

    【讨论】:

    • 有没有办法只用 1 个模板参数来实现这一点? std::unordered_set&lt;Point&gt; points
    【解决方案3】:

    我同意sjrowlinson 对于您的特定用例,std::unordered_map&lt;std::string, double&gt; 可能是更好的选择。但是,如果由于某种原因您想坚持使用unordered_set,那么您也可以使用lambda expression 而不是定义哈希函数。但是您还必须提供比较功能 (equal) 才能使您的代码正常工作。如果您希望两个node 实例具有相同的node_id,则它们相等,那么您可以使用以下代码:

    auto hash = [](const node& n){ return std::hash<std::string>()(n.node_id); };
    auto equal = [](const node& n1, const node& n2){ return n1.node_id == n2.node_id; };
    std::unordered_set<node, decltype(hash), decltype(equal)> set(8, hash, equal);
    

    但是,如果您想使用std::unordered_set::find(),那么您不能简单地为该函数提供一个字符串(例如"1001"),因为它需要一个node 对象作为参数。不过,下面的代码(它会创建一个临时对象)可以解决问题:

    set.insert(node("1001", 100));
    if (set.find(node("1001", 0)) != set.end())
        std::cout << "1001 found" << std::endl;
    

    请注意,输出 1001 found 已打印,尽管插入的 nodevalue 与赋予 find() 函数的 nodevalue 不同(分别为 100 和 0 )。这是因为比较函数equal 在检查相等性时只考虑node_id

    Code on Ideone

    【讨论】:

      【解决方案4】:

      您需要实现一个自定义哈希函数(我建议使用 Boost 库中的函数)来执行此操作。 C++ 允许您使用 unordered_set 保存指向类对象的指针。对于大多数目的,这应该可以解决问题。

      【讨论】:

      • 我能问一下保存指针而不是对象有什么好处吗?总是想知道
      • 就散列而言,指针只是指向内存的值——内存地址,如果你愿意,可以像任何其他数字(如 int、double 等)一样散列,因为它们本身是相似的。在大局中,指针是引用,在传递时避免多余的副本。
      【解决方案5】:

      正如How to specialize std::hash<Key>::operator() for user-defined type in unordered containers? 中所指出的,另一种选择是引入 == 运算符并将专门化注入 std::hash

      【讨论】:

        猜你喜欢
        • 2012-04-01
        • 1970-01-01
        • 2023-03-11
        • 1970-01-01
        • 2020-03-26
        • 2013-09-09
        • 1970-01-01
        • 2020-03-15
        相关资源
        最近更新 更多