如何哈希QVariant?

时间:2016-12-14 14:25:55

标签: c++ qt qvariant stdhash

我需要使用QList<QVariant>作为std::unordered_map的密钥。这样做的目的是通过在唯一键列上建立索引来优化对数据表的搜索。

所以我制作了这段代码。它不完整,但列出了表键列中出现的一些基本数据类型:

#include <unordered_map>
#include <string>
//std::hash
#include <functional>
//std::size_t
#include <cstddef>
// Hashing method for QVariantList
namespace std {
    template <>
    struct hash<QList<QVariant>>
    {
        std::size_t operator()(const QList<QVariant>& k) const
        {
            using std::size_t;
            using std::hash;
            using std::string;
            size_t hash_num = 0;
            Q_FOREACH(var, k) {
                // Make hash of the primitive value of the QVariant
                switch(var.type()) {
                    case QVariant::String : {
                        hash_num = hash_num^hash<string>(var.toString().toStdString());
                        break;
                    }
                    case QVariant::Char :
                    case QVariant::ULongLong :
                    case QVariant::UInt :
                    case QVariant::LongLong :
                    case QVariant::Int : {
                        hash_num = hash_num^hash<long long>(var.toLongLong());
                        break;
                    }
                    case QVariant::Double : {
                        hash_num = hash_num^hash<double>(var.toDouble());
                        break;
                    }
                }
            }
            return hash_num;
        }
    };
}

显然,我不喜欢整个switch的事情。这是一个非常漫长而丑陋的代码,并且仅考虑基本类型。我宁愿对为QVariant的内部数据分配的内存数据进行哈希处理。或者,甚至更好 - 使用一些Qt的散列方法。

是否存在一种半可靠的方法来散列任何QVariant而不将其转换为原始类型?

*我知道复杂的物体可能隐藏在QVariant后面,但这会导致碰撞的情况非常罕见,所以我不必在意。

1 个答案:

答案 0 :(得分:7)

给自己QByteArray + QBuffer + QDataStream基本序列化QVariantQByteArray

然后简单地散列字节数组中的原始字节。 Qt已经为qHash实现了QByteArray函数,所以你已经完成了设置。

您可以通过重复使用具有足够预分配字节的相同QByteArray来最大限度地提高效率,以避免重新分配。您可以将整个事物包装在VariantHasher类中,并在每次新散列之前简单地seek(0)填充缓冲区,并且只散列pos()个字节数而不是整个事件。

class QVariantHasher {
  public:
    QVariantHasher() : buff(&bb), ds(&buff) {
      bb.reserve(1000);
      buff.open(QIODevice::WriteOnly);
    }
    uint hash(const QVariant & v) {
      buff.seek(0);
      ds << v;
      return qHashBits(bb.constData(), buff.pos());
    }
  private:
    QByteArray bb;
    QBuffer buff;
    QDataStream ds;
};

评论中提到它非常快,并且它具有处理支持QDataStream序列化的每种类型的优点。对于自定义类型,您只需实现序列化,无需制作和维护巨型交换机。如果您已经实现了交换机版本,则可以进行比较。交换机本身有很多分支,而重用相同的字节数组对缓存非常友好,特别是如果你不使用很多字节,也就是说,你不是包含很长字符串或数组的散列变种。

此外,它优于半可靠,因为散列也包括变量类型,因此即使在实际数据可能是二进制相同的情况下,例如,两个字节的值为255,而短的值为65535,哈希将包含类型,因此值不会发生冲突。