使用自定义键在std :: unordered_map中查找键

时间:2016-07-13 04:42:41

标签: c++ unordered-map

我目前正在使用自定义键创建自定义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);

即使已插入密钥,clientIter也始终指向结束迭代器位置。您是否认为这与必须在堆栈上重新创建这些ClientKey值然后将它们传递到地图以进行查找有关,或者我在其他地方遇到问题?感谢您的澄清和见解。

2 个答案:

答案 0 :(得分:0)

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

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

对于k0和k1,emtpyId标志是否有任何特殊原因?我个人会说:

  1. 标记实现不正确。
  2. 这是多余的,您可以通过m_clientId.empty()获得相同的信息。
  3. 现在失败的原因:

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

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

    想象一下k0已插入地图中,而k1则会尝试找到它。会发生什么? k1生成另一个散列键而不是k0,并且映射将查看与k0所在位置不同的桶 - 并且不会找到任何内容。

    我认为您想要实现的是为同一连接ID提供多个客户端,并且能够针对给定的连接ID迭代这些客户端。所以您可能更喜欢std::unordered_multimap<std::string, ClientPtr>(其中string参数表示连接ID)。您将通过equal_range获取给定连接ID的所有客户,然后您的班级ClientKey就会过时。

答案 1 :(得分:0)

您的代码允许以下内容返回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真的应该是真的吗?