QHash/QMultiHash: fix operator==
The existing QHash::operator== does not work when the same keys appear in different order between the two hashes being compared. However, relying on iteration order on a QHash is (as usual) a bad idea and one should never do it. Task-number: QTBUG-60395 Change-Id: Ifb39a6779230e26bbd6fdba82ccc0247b9cdc6ed Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
18d49808db
commit
dbd55cdaf3
@ -51,6 +51,8 @@
|
||||
#include <initializer_list>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(Q_CC_MSVC)
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 4311 ) // disable pointer truncation warning
|
||||
@ -936,18 +938,24 @@ Q_OUTOFLINE_TEMPLATE bool QHash<Key, T>::operator==(const QHash &other) const
|
||||
const_iterator it = begin();
|
||||
|
||||
while (it != end()) {
|
||||
const Key &akey = it.key();
|
||||
// Build two equal ranges for i.key(); one for *this and one for other.
|
||||
// For *this we can avoid a lookup via equal_range, as we know the beginning of the range.
|
||||
auto thisEqualRangeEnd = it;
|
||||
while (thisEqualRangeEnd != end() && it.key() == thisEqualRangeEnd.key())
|
||||
++thisEqualRangeEnd;
|
||||
|
||||
const_iterator it2 = other.find(akey);
|
||||
do {
|
||||
if (it2 == other.end() || !(it2.key() == akey))
|
||||
return false;
|
||||
if (!(it.value() == it2.value()))
|
||||
return false;
|
||||
++it;
|
||||
++it2;
|
||||
} while (it != end() && it.key() == akey);
|
||||
const auto otherEqualRange = other.equal_range(it.key());
|
||||
|
||||
if (std::distance(it, thisEqualRangeEnd) != std::distance(otherEqualRange.first, otherEqualRange.second))
|
||||
return false;
|
||||
|
||||
// Keys in the ranges are equal by construction; this checks only the values.
|
||||
if (!std::is_permutation(it, thisEqualRangeEnd, otherEqualRange.first))
|
||||
return false;
|
||||
|
||||
it = thisEqualRangeEnd;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ private slots:
|
||||
void contains(); // copied from tst_QMap
|
||||
void qhash();
|
||||
void take(); // copied from tst_QMap
|
||||
void operator_eq(); // copied from tst_QMap
|
||||
void operator_eq(); // slightly modified from tst_QMap
|
||||
void rehash_isnt_quadratic();
|
||||
void dont_need_default_constructor();
|
||||
void qmultihash_specific();
|
||||
@ -771,7 +771,7 @@ void tst_QHash::take()
|
||||
QVERIFY(!map.contains(3));
|
||||
}
|
||||
|
||||
//copied from tst_QMap
|
||||
// slightly modified from tst_QMap
|
||||
void tst_QHash::operator_eq()
|
||||
{
|
||||
{
|
||||
@ -848,6 +848,71 @@ void tst_QHash::operator_eq()
|
||||
QVERIFY(a != b);
|
||||
QVERIFY(!(a == b));
|
||||
}
|
||||
|
||||
// unlike multi-maps, multi-hashes should be equal iff their contents are equal,
|
||||
// regardless of insertion or iteration order
|
||||
|
||||
{
|
||||
QHash<int, int> a;
|
||||
QHash<int, int> b;
|
||||
|
||||
a.insertMulti(0, 0);
|
||||
a.insertMulti(0, 1);
|
||||
|
||||
b.insertMulti(0, 1);
|
||||
b.insertMulti(0, 0);
|
||||
|
||||
QVERIFY(a == b);
|
||||
QVERIFY(!(a != b));
|
||||
}
|
||||
|
||||
{
|
||||
QHash<int, int> a;
|
||||
QHash<int, int> b;
|
||||
|
||||
enum { Count = 100 };
|
||||
|
||||
for (int key = 0; key < Count; ++key) {
|
||||
for (int value = 0; value < Count; ++value)
|
||||
a.insertMulti(key, value);
|
||||
}
|
||||
|
||||
for (int key = Count - 1; key >= 0; --key) {
|
||||
for (int value = 0; value < Count; ++value)
|
||||
b.insertMulti(key, value);
|
||||
}
|
||||
|
||||
QVERIFY(a == b);
|
||||
QVERIFY(!(a != b));
|
||||
}
|
||||
|
||||
{
|
||||
QHash<int, int> a;
|
||||
QHash<int, int> b;
|
||||
|
||||
enum {
|
||||
Count = 100,
|
||||
KeyStep = 17, // coprime with Count
|
||||
ValueStep = 23, // coprime with Count
|
||||
};
|
||||
|
||||
for (int key = 0; key < Count; ++key) {
|
||||
for (int value = 0; value < Count; ++value)
|
||||
a.insertMulti(key, value);
|
||||
}
|
||||
|
||||
// Generates two permutations of [0, Count) for the keys and values,
|
||||
// so that b will be identical to a, just built in a very different order.
|
||||
|
||||
for (int k = 0; k < Count; ++k) {
|
||||
const int key = (k * KeyStep) % Count;
|
||||
for (int v = 0; v < Count; ++v)
|
||||
b.insertMulti(key, (v * ValueStep) % Count);
|
||||
}
|
||||
|
||||
QVERIFY(a == b);
|
||||
QVERIFY(!(a != b));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QHash::compare()
|
||||
|
Loading…
Reference in New Issue
Block a user