From 6139fbeb5f41843e524ec40ce5be57f0df23e922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Martins?= Date: Sun, 7 Feb 2016 02:49:33 +0000 Subject: [PATCH] Introduce QHash::equal_range() Similar to QMap::equal_range(). Will allow to easily fix inefficient code such as: foreach (auto value, hash.values(key)) { ... } [ChangeLog][QtCore][QHash] Added QHash::equal_range() Change-Id: I6e19e25de632e897ad83d3141d9d07f0313f7200 Reviewed-by: Marc Mutz --- src/corelib/tools/qhash.cpp | 13 +++ src/corelib/tools/qhash.h | 35 ++++++ tests/auto/corelib/tools/qhash/tst_qhash.cpp | 108 +++++++++++++++++++ 3 files changed, 156 insertions(+) diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index c1a1b9715f0..b49c9d683ab 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -1803,6 +1803,19 @@ uint qHash(long double key, uint seed) Q_DECL_NOTHROW returns \c false. */ +/*! \fn QPair QHash::equal_range(const Key &key) + \since 5.7 + + Returns a pair of iterators delimiting the range of values \c{[first, second)}, that + are stored under \a key. If the range is empty then both iterators will be equal to end(). +*/ + +/*! + \fn QPair QHash::equal_range(const Key &key) const + \overload + \since 5.7 +*/ + /*! \typedef QHash::ConstIterator Qt-style synonym for QHash::const_iterator. diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 819631932b4..5e3016d3133 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -458,6 +458,8 @@ public: inline key_iterator keyBegin() const { return key_iterator(begin()); } inline key_iterator keyEnd() const { return key_iterator(end()); } + QPair equal_range(const Key &key); + QPair equal_range(const Key &key) const Q_DECL_NOTHROW; iterator erase(iterator it) { return erase(const_iterator(it.i)); } iterator erase(const_iterator it); @@ -944,6 +946,39 @@ Q_OUTOFLINE_TEMPLATE bool QHash::operator==(const QHash &other) const return true; } +template +QPair::iterator, typename QHash::iterator> QHash::equal_range(const Key &akey) +{ + detach(); + auto pair = qAsConst(*this).equal_range(akey); + return qMakePair(iterator(pair.first.i), iterator(pair.second.i)); +} + +template +QPair::const_iterator, typename QHash::const_iterator> QHash::equal_range(const Key &akey) const Q_DECL_NOTHROW +{ + uint h; + Node *node = *findNode(akey, &h); + const_iterator firstIt = const_iterator(node); + + if (node != e) { + // equal keys must hash to the same value and so they all + // end up in the same bucket. So we can use node->next, + // which only works within a bucket, instead of (out-of-line) + // QHashData::nextNode() + while (node->next != e && node->next->key == akey) + node = node->next; + + // 'node' may be the last node in the bucket. To produce the end iterator, we'd + // need to enter the next bucket in this case, so we need to use + // QHashData::nextNode() here, which, unlike node->next above, can move between + // buckets. + node = concrete(QHashData::nextNode(reinterpret_cast(node))); + } + + return qMakePair(firstIt, const_iterator(node)); +} + template class QMultiHash : public QHash { diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index c62943febc0..4336d02b2c0 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -32,6 +32,7 @@ #include #include +#include class tst_QHash : public QObject { @@ -65,6 +66,7 @@ private slots: void twoArguments_qHash(); void initializerList(); void eraseValidIteratorOnSharedHash(); + void equal_range(); }; struct IdentityTracker { @@ -1355,5 +1357,111 @@ void tst_QHash::eraseValidIteratorOnSharedHash() QCOMPARE(itemsWith10, 3); } +void tst_QHash::equal_range() +{ + QHash hash; + + auto result = hash.equal_range(0); + QCOMPARE(result.first, hash.end()); + QCOMPARE(result.second, hash.end()); + + hash.insert(1, "one"); + + result = hash.equal_range(1); + + QCOMPARE(result.first, hash.find(1)); + QVERIFY(std::distance(result.first, result.second) == 1); + + QHash h1; + { + auto p = h1.equal_range(0); + QVERIFY(p.first == p.second); + QVERIFY(p.first == h1.end()); + } + + h1.insert(1, 2); + { + auto p1 = h1.equal_range(9); + QVERIFY(p1.first == p1.second); + QVERIFY(p1.first == h1.end()); + } + { + auto p2 = h1.equal_range(1); + QVERIFY(p2.first != p2.second); + QVERIFY(p2.first == h1.begin()); + QVERIFY(p2.second == h1.end()); + } + + QMultiHash m1 = h1; + m1.insert(1, 0); + QCOMPARE(m1.size(), 2); + { + auto p1 = m1.equal_range(9); + QVERIFY(p1.first == p1.second); + QVERIFY(p1.first == m1.end()); + } + { + auto p2 = m1.equal_range(1); + QVERIFY(p2.first != p2.second); + QVERIFY(p2.first == m1.begin()); + QVERIFY(p2.second == m1.end()); + QCOMPARE(std::distance(p2.first, p2.second), 2); + } + + m1.insert(0, 0); + QCOMPARE(m1.size(), 3); + { + auto p1 = m1.equal_range(9); + QVERIFY(p1.first == p1.second); + QVERIFY(p1.first == m1.end()); + } + { + const auto p2 = m1.equal_range(1); + QVERIFY(p2.first != p2.second); + QCOMPARE(p2.first.key(), 1); + QCOMPARE(std::distance(p2.first, p2.second), 2); + QVERIFY(p2.first == m1.begin() || p2.second == m1.end()); + } + + const QHash ch1 = h1; + { + auto p1 = ch1.equal_range(9); + QVERIFY(p1.first == p1.second); + QVERIFY(p1.first == ch1.end()); + } + { + auto p2 = ch1.equal_range(1); + QVERIFY(p2.first != p2.second); + QVERIFY(p2.first == ch1.begin()); + QVERIFY(p2.second == ch1.end()); + } + + const QMultiHash cm1 = m1; + { + auto p1 = cm1.equal_range(9); + QVERIFY(p1.first == p1.second); + QVERIFY(p1.first == cm1.end()); + } + { + auto p2 = cm1.equal_range(1); + QVERIFY(p2.first != p2.second); + QCOMPARE(std::distance(p2.first, p2.second), 2); + QVERIFY(p2.first == cm1.cbegin() || p2.second == cm1.cend()); + } + + QHash h2; + for (int i = 0; i < 8; ++i) + for (int j = 0; j < 8; ++j) + h2.insertMulti(i, i*j); + + for (int i = 0; i < 8; ++i) { + auto pair = h2.equal_range(i); + std::vector vec(pair.first, pair.second); + std::sort(vec.begin(), vec.end()); + for (int j = 0; j < 8; ++j) + QCOMPARE(i*j, vec[j]); + } +} + QTEST_APPLESS_MAIN(tst_QHash) #include "tst_qhash.moc"