如何为自定义类使用C++ unordered_set?

day*_*yup 8 c++ hash-function unordered-set

如何在一个类中存储类的对象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;
}
Run Code Online (Sandbox Code Playgroud)

cda*_*hms 26

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

// 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法只用 1 个模板参数来实现这一点?`std::unordered_set&lt;Point&gt; 点` (4认同)

sjr*_*son 6

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

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;
Run Code Online (Sandbox Code Playgroud)

但是,正如其中一条评论指出的那样,你可能最好在std::unordered_map<std::string, double>这里使用.


hon*_*onk 6

我同意sjrowlinson 的观点,对于您的特定用例,std::unordered_map<std::string, double>可能是更好的选择。unordered_set但是,如果由于某种原因您想坚持使用 an ,那么您也可以使用lambda 表达式而不是定义哈希函数。但您还必须提供比较函数 ( 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);
Run Code Online (Sandbox Code Playgroud)

但是,如果您想使用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;
Run Code Online (Sandbox Code Playgroud)

请注意,尽管插入的值与函数给定的值不同(分别为 100 和 0),但输出1001 found仍被打印。这是因为比较函数仅在检查相等性时考虑。valuenodevaluenodefind()equalnode_id

Ideone 上的代码