【问题标题】:How to hash QVariant?如何散列 QVariant?
【发布时间】:2016-12-14 14:25:55
【问题描述】:

我需要使用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 后面,但这会导致碰撞的情况非常少见,所以我不必在意。

【问题讨论】:

  • 你试过Qt的方法QVariant::toHash()吗?
  • @Jean-Emmanuel It looks like 将变体转换为 QHash 并且不散列基础值。
  • @Jean-Emmanuel toHash 尝试将 QVariant 转换为 QHash&lt;QString, QVariant&gt;,这是一个哈希映射,而不是一个哈希。我的意思是,是的,我确实在文档上按了 Ctrl+F,另外我还阅读了它所说的内容。
  • 如果您不担心性能:使用QDataStreamQVariant 写入QByteArray 并使用qHash(const QByteArray &amp;) 函数。我还没有看到一种简单的方法来获得QVariant 的“原始”表示。
  • 你可以使用像this 这样的访问者,然后将返回的值传递给qHash()

标签: c++ qt qvariant stdhash


【解决方案1】:

给自己一个QByteArray + QBuffer + QDataStream 基本上将QVariants 序列化为QByteArray

然后简单地散列字节数组中的原始字节。 Qt 已经为QByteArray 实现了qHash 函数,所以一切就绪。

您可以通过重用具有足够预分配字节数的相同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;
};

正如 cmets 中提到的那样,它非常快,并且它的优点是可以处理支持QDataStream 序列化的所有类型。对于自定义类型,您只需实现序列化,无需进行和维护巨大的开关。如果您已经实现了切换版本,那么进行比较会很有趣。开关本身有很多分支,而重用相同的字节数组对缓存非常友好,特别是如果您不使用很多字节,也就是说,您不会散列包含很长字符串或数组的变体。

此外,它比半可靠更好,因为散列也包括变体类型,所以即使在实际数据可能是二进制相同的情况下,例如两个字节的值为 255 与一个值为 65535 的短字节,哈希将合并类型,因此值不会发生冲突。

【讨论】:

  • 在这种特殊情况下,我确实关心性能。你认为你的提议会比我提出的switch 慢吗?
  • 好吧,除非您将它们并排比较,否则无法判断。你需要多快?在我的系统上,它在 15213 纳秒内散列 4 个整数 4 个实数 4 个字符串和 4 个 QRect。还有一个便利因素,你不必实现一个巨大的开关,它适用于所有支持序列化的类型。
  • @TomášZato 首先,如果您将使用qHash 函数,您可以使您的switch 对Qt 更友好。例如。 var.toString().toStdString() 可能比qHash(var.toString()) 慢,因为您创建了一个在堆中分配的临时对象。第二:只是尝试和比较。没有人知道QDataStream 是如何序列化QVariant 的。至少额外的QByteArray 可能会有很长的分配/解除分配时间。
  • @TomášZato 您可以使用问题中的代码专门处理最常见的类型,然后回退到此答案的方法,作为您不想打扰的类型的后备和面向未来特殊处理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-22
  • 2017-10-13
  • 2011-10-06
  • 2011-03-12
  • 2010-09-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多