QHash 在不同的 qt 版本中表现不同

Posted

技术标签:

【中文标题】QHash 在不同的 qt 版本中表现不同【英文标题】:QHash behaving different in different qt version 【发布时间】:2020-08-11 07:39:31 【问题描述】:

我在 QT 4.8 和 5.12.9 中编译下面的代码。

QHash<QString, int> m_userDataHash;
m_userDataHash.insert("white", 1);
m_userDataHash.insert("yellow", 3);
m_userDataHash.insert("lightblue", 5);
m_userDataHash.insert("darkblue", 7);
m_userDataHash.insert("pink", 9);
m_userDataHash.insert("red", 11);
m_userDataHash.insert("green", 13);
m_userDataHash.insert("black", 15);
m_userDataHash.insert("grey", 17);

QHashIterator<QString, int> i(m_userDataHash);
while (i.hasNext())

    i.next();
    ui->ColorCombo->addItem(i.key());

此代码的行为不同,因为在不同的 qt 版本中插入顺序不同。

在 Qt 5.12.9 中

在 Qt 4.8 中

我该如何解决这个问题?为什么会这样?

我检查了 QHash 文档,但什么也想不通。 https://doc.qt.io/qt-5/qhash.html#insert

【问题讨论】:

回复。 "How can I solve this problem?": 什么问题?如果您需要特定订单,请使用支持该订单的容器。 @G.M.我可以使用 QVector 来解决这个问题。但希望对代码进行最小的更改 【参考方案1】:

一个可能的罪魁祸首是 2012 年的 addition of randomized hashing。source:

所有哈希表都容易受到特定类别的拒绝服务攻击,在这种攻击中,攻击者会仔细预先计算一组不同的密钥,这些密钥将在哈希表的同一个桶中进行哈希处理(甚至具有非常相同的哈希值)。攻击的目的是在将数据输入表时获得最坏情况下的算法行为(O(n) 而不是摊销的 O(1),详见算法复杂性)。

为了避免这种最坏情况的行为,由 qHash() 完成的哈希值的计算可以用随机种子加盐,从而使攻击范围无效。这个种子由 QHash 每个进程自动生成一次,然后由 QHash 作为 qHash() 函数的双参数重载的第二个参数传递。

默认情况下启用 QHash 的随机化。尽管程序不应该依赖于特定的 QHash 排序,但在某些情况下,您可能会暂时需要确定性行为,例如调试或回归测试。要禁用随机化,请将环境变量 QT_HASH_SEED 定义为值为 0。或者,您可以调用值为 0 的 qSetGlobalQHashSeed() 函数。

正如文档所述,您不应依赖哈希表中的条目顺序。对于调试或测试,您可以尝试将 QT_HASH_SEED 设置为 0 以查看它是否重现了旧行为。

【讨论】:

【参考方案2】:

QT 文档指出:

当迭代 QMap 时,项目总是按键排序。和 QHash,物品随意排列。

这是散列函数的预期行为。他们不按任何顺序存储项目,但是您可以足够快地获得任何项目 O(1)。 Maps 将键存储在树中,您可以按顺序对其进行迭代,但查找的是 log(N)。

哈希函数的实现可能已经改变,你得到不同的顺序。这就是这里发生的事情。

如果地图中的项目很少,它可能比散列更快。也许你可以用 QMap 替换 QHash。

如果您有很多项目并且需要非常快速的查找(因此 QHash 是您的最佳选择),那么您可以将有序键单独存储在向量中并使用它进行迭代。

【讨论】:

OP 不质疑随机顺序;他们很惊讶 Qt 版本之间的伪随机顺序存在差异。 好吧,根据 OP 的 cmets,他们希望代码的更改最少。使用 QMap 就可以做到这一点,因为它的方法几乎与 QHash 相同。也许我对OP关于如何解决这个问题的问题有更广泛的看法。我投票赞成你的答案,因为它对正在发生的事情进行了深入的解释。 “我想要最小的改变”编辑现在更适合您的答案。

以上是关于QHash 在不同的 qt 版本中表现不同的主要内容,如果未能解决你的问题,请参考以下文章

为啥||或者在rails中表现不同? [复制]

为啥相同的 MySql 查询在代码和工作台中表现不同?

为啥在具有一级索引的 MultiIndex 列的 pandas DataFrame 中表现不同?

相同的自动布局约束在纵向和横向中表现不同

什么可以使程序在`strace`中表现不同?

echo -e 在SHELL脚本和命令行中表现不同一例问题排查