【问题标题】:Finding Key in std::unordered_map with custom key使用自定义键在 std::unordered_map 中查找键
【发布时间】:2016-11-15 12:24:48
【问题描述】:

我目前正在使用我的自定义键创建自定义 std::unordered_map 声明:

class BASE_DLLSPEC ClientKey
{
  private:
    // this is always true initially until we call SetClientId
    bool emptyId;

    // both of these are guaranteed to be unique
    QString m_connectId; // ip:port format
    QString m_clientId;  // {Uuid} format
    // ----------

  public:
    ClientKey(const QString& connectId = "", const QString& clientId = "") :
      emptyId(true), m_connectId(connectId), m_clientId(clientId)
    { }

    void SetClientId(const QString& clientId)
    {
      m_clientId = clientId;
      emptyId    = false;
    }

    const QString& GetConnectId() const { return m_connectId; }
    const QString& GetClientId() const { return m_clientId; }

    bool operator==(const ClientKey& other) const
    {
      int comp1 = QString::compare(m_connectId, other.GetConnectId());
      int comp2 = QString::compare(m_clientId, other.GetClientId());

      return (comp1 == 0) ||
             (!emptyId && comp2 == 0);
    }
};

struct BASE_DLLSPEC ClientKeyHash
{
  std::size_t operator()(const ClientKey& key) const
  {
    std::string connectId = key.GetConnectId().toStdString();
    std::string clientId  = key.GetClientId().toStdString();

    std::size_t h1 = std::hash<std::string>()(connectId);
    std::size_t h2 = std::hash<std::string>()(clientId);
    return h1 ^ (h2 << 1);
  }
};

struct BASE_DLLSPEC ClientKeyEqual
{
  bool operator()(const ClientKey& lhs, const ClientKey& rhs) const
  {
    return lhs == rhs;
  }
};

typedef std::unordered_map<ClientKey,
                           ClientPtr,
                           ClientKeyHash,
                           ClientKeyEqual> ClientMap;

我在迭代过程中很难找到特定的键。由于某种原因,当我传入一个键进行查找时,我的客户端对象永远不会被找到。

ClientKey key = Manager::ClientKey(connectId);
ClientManager& clientManager = Manager::ClientManager::GetInstance();
ClientMap::const_iterator clientIter = clientManager.GetClients().find(key);

即使key已经被插入,clientIter总是指向结束迭代器的位置。您是否认为这与必须在堆栈上重新创建这些 ClientKey 值然后将它们传递到映射中进行查找有关,还是我在其他地方有问题?感谢您的澄清和见解。

【问题讨论】:

  • 我没有看到您为您的客户端密钥实现哈希函数,这很可能是问题所在。看到这个答案:stackoverflow.com/questions/17016175/…
  • 看起来您允许比较相等的键具有不同的哈希值。这不可能。

标签: c++ unordered-map


【解决方案1】:

首先,对 emptyId 字段的一些注意事项(不要考虑无效格式 - 顺便说一下,您也不会检查):

ClientKey k0("hello", "world");
ClientKey k1("hello");
k1.SetClientId("world");

emtpyId 标志对于 k0 和 k1 应该不同有什么特别的原因吗?我个人会说:

  1. 标志实施不正确。
  2. 这是多余的,你通过m_clientId.empty()得到相同的信息。

现在失败的原因:

再次考虑 k0k1,但没有在 k1 上调用 SetClientId:

ClientKey k0("hello", "world");
ClientKey k1("hello");

想象k0 已被插入到地图中,而k1 你试图找到它。会发生什么? k1 会生成另一个哈希键,而不是 k0,并且映射将查看与 k0 所在位置不同的存储桶 - 并且不会找到任何东西。

我认为您想要实现的是拥有多个具有相同连接 ID 的客户端,并且能够针对给定的连接 ID 对这些客户端进行迭代。所以你可能更喜欢std::unordered_multimap&lt;std::string, ClientPtr&gt;(其中字符串参数代表连接ID)。然后,您将通过 equal_range 获取给定连接 ID 的所有客户端,并且您的类 ClientKey 已过时。

【讨论】:

  • 这确实是我遇到这个问题的原因。非常感谢。我最初只传入带有空 clientId 的 connectId,我在其中进行查找。其次是设置clientId的新客户端。因此,在重新迭代时,clientId 始终为空,并且在当前状态下,哈希也会在查找时使用 clientId。
【解决方案2】:

您的代码允许以下内容返回 true:

ClientKey k1("hello", "world");
ClientKey k2("hello", "");
return k1 == k2;

但是,您的哈希是基于 connectId 和 clientId 的组合。

unordered_map::find 不会对映射进行详尽的搜索,而是在存储桶中查找给定的哈希值并比较 存储桶中的条目。

您仅使用connectId 生成测试密钥,因此它在存储桶中查找ClientKey(connectId, "") 而不是ClientKey(connectId, someOtherValue) 的存储桶。

您应该考虑仅根据connectId 制作哈希。

最后,注意你的构造函数:

ClientKey(const QString& connectId = "", const QString& clientId = "") :
  emptyId(true), m_connectId(connectId), m_clientId(clientId)
{ }

如果我写:

ClientKey ck("hello");

emptyId 真的应该是真的吗?

【讨论】:

    猜你喜欢
    • 2021-10-23
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 2018-07-09
    • 1970-01-01
    • 2014-12-28
    • 2015-09-02
    • 2011-12-28
    相关资源
    最近更新 更多