QMap - add insert overload that provide a hint

This adds a fast insert on QMap when providing a correct hint.

Change-Id: I256bba342932c1d4f24c6e65074e1bf47b519537
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Thorbjørn Lund Martsum 2012-10-05 15:58:56 +02:00 committed by The Qt Project
parent 49a2ec05b4
commit ca6a4258d0
4 changed files with 236 additions and 0 deletions

View File

@ -981,6 +981,32 @@ void QMapDataBase::freeData(QMapDataBase *d)
\sa insertMulti()
*/
/*! \fn QMap::iterator QMap::insert(const_iterator pos, const Key &key, const T &value)
\overload
\since 5.1
Inserts a new item with the key \a key and value \a value and with hint \a pos
suggesting where to do the insert.
If constBegin() is used as hint it indicates that the \a key is less than any key in the map
while constEnd() suggests that the \a key is (strictly) larger than any key in the map.
Otherwise the hint should meet the condition (\a pos - 1).key() < \a key <= pos.key().
If the hint \a pos is wrong it is ignored and a regular insert is done.
If there is already an item with the key \a key, that item's value
is replaced with \a value.
If there are multiple items with the key \a key, then exactly one of them
is replaced with \a value.
When creating a map from sorted data inserting the largest key first with constBegin()
is faster than inserting in sorted order with constEnd()
\b {Note:} Be careful with the hint. Providing an iterator from an older shared instance might
crash but there is also a risk that it will silently corrupt both the map and the \a pos map.
\sa insertMulti()
*/
/*! \fn QMap::iterator QMap::insertMulti(const Key &key, const T &value)
Inserts a new item with the key \a key and a value of \a value.

View File

@ -538,6 +538,7 @@ public:
iterator upperBound(const Key &key);
const_iterator upperBound(const Key &key) const;
iterator insert(const Key &key, const T &value);
iterator insert(const_iterator pos, const Key &key, const T &value);
iterator insertMulti(const Key &key, const T &value);
QMap<Key, T> &unite(const QMap<Key, T> &other);
@ -662,6 +663,72 @@ Q_INLINE_TEMPLATE typename QMap<Key, T>::iterator QMap<Key, T>::insert(const Key
return iterator(z);
}
template <class Key, class T>
typename QMap<Key, T>::iterator QMap<Key, T>::insert(const_iterator pos, const Key &akey, const T &avalue)
{
if (d->ref.isShared())
return this->insert(akey, avalue);
if (pos == constEnd()) {
// Hint is that the Node is larger than (or equal to) the largest value.
Node *n = static_cast<Node *>(pos.i->left);
if (n) {
while (n->right)
n = static_cast<Node *>(n->right);
if (!qMapLessThanKey(n->key, akey))
return this->insert(akey, avalue); // ignore hint
// This can be optimized by checking equal too.
// we can overwrite if previous node key is strictly smaller
// (or there is no previous node)
Node *z = d->createNode(akey, avalue, n, false); // insert right most
return iterator(z);
}
return this->insert(akey, avalue);
} else {
// Hint indicates that the node should be less (or equal to) the hint given
// but larger than the previous value.
Node *next = const_cast<Node*>(pos.i);
if (qMapLessThanKey(next->key, akey))
return this->insert(akey, avalue); // ignore hint
if (pos == constBegin()) {
// There is no previous value
// Maybe overwrite left most value
if (!qMapLessThanKey(akey, next->key)) {
next->value = avalue; // overwrite current iterator
return iterator(next);
}
// insert left most.
Node *z = d->createNode(akey, avalue, begin().i, true);
return iterator(z);
} else {
Node *prev = const_cast<Node*>(pos.i->previousNode());
if (!qMapLessThanKey(prev->key, akey)) {
return this->insert(akey, avalue); // ignore hint
}
// Hint is ok
if (!qMapLessThanKey(akey, next->key)) {
next->value = avalue; // overwrite current iterator
return iterator(next);
}
// we need to insert (not overwrite)
if (prev->right == 0) {
Node *z = d->createNode(akey, avalue, prev, false);
return iterator(z);
}
if (next->left == 0) {
Node *z = d->createNode(akey, avalue, next, true);
return iterator(z);
}
Q_ASSERT(false); // We should have prev->right == 0 or next->left == 0.
return this->insert(akey, avalue);
}
}
}
template <class Key, class T>
Q_INLINE_TEMPLATE typename QMap<Key, T>::iterator QMap<Key, T>::insertMulti(const Key &akey,
const T &avalue)

View File

@ -85,6 +85,7 @@ private slots:
void insert();
void checkMostLeftNode();
void initializerList();
void testInsertWithHint();
};
typedef QMap<QString, QString> StringMap;
@ -1159,5 +1160,69 @@ void tst_QMap::initializerList()
#endif
}
void tst_QMap::testInsertWithHint()
{
QMap<int, int> map;
map.setSharable(false);
// Check with end hint();
map.insert(map.constEnd(), 3, 1); // size == 1
sanityCheckTree(map, __LINE__);
map.insert(map.constEnd(), 5, 1); // size = 2
sanityCheckTree(map, __LINE__);
map.insert(map.constEnd(), 50, 1); // size = 3
sanityCheckTree(map, __LINE__);
QMap<int, int>::const_iterator key75(map.insert(map.constEnd(), 75, 1)); // size = 4
sanityCheckTree(map, __LINE__);
map.insert(map.constEnd(), 100, 1); // size = 5
sanityCheckTree(map, __LINE__);
map.insert(map.constEnd(), 105, 1); // size = 6
sanityCheckTree(map, __LINE__);
map.insert(map.constEnd(), 10, 5); // invalid hint and size = 7
sanityCheckTree(map, __LINE__);
QMap<int, int>::iterator lastkey = map.insert(map.constEnd(), 105, 12); // overwrite
sanityCheckTree(map, __LINE__);
QCOMPARE(lastkey.value(), 12);
QCOMPARE(lastkey.key(), 105);
QCOMPARE(map.size(), 7);
// With regular hint
map.insert(key75, 75, 100); // overwrite current key
sanityCheckTree(map, __LINE__);
QCOMPARE(map.size(), 7);
QCOMPARE(key75.key(), 75);
QCOMPARE(key75.value(), 100);
map.insert(key75, 50, 101); // overwrite previous value
QMap<int, int>::const_iterator key50(key75);
--key50;
QCOMPARE(map.size(), 7);
QCOMPARE(key50.key(), 50);
QCOMPARE(key50.value(), 101);
map.insert(key75, 17, 125); // invalid hint - size 8
sanityCheckTree(map, __LINE__);
QCOMPARE(map.size(), 8);
// begin
map.insert(map.constBegin(), 1, 1); // size 9
sanityCheckTree(map, __LINE__);
QCOMPARE(map.size(), 9);
map.insert(map.constBegin(), 1, 10); // overwrite existing (leftmost) value
QCOMPARE(map.constBegin().value(), 10);
map.insert(map.constBegin(), 47, 47); // wrong hint - size 10
sanityCheckTree(map, __LINE__);
QCOMPARE(map.size(), 10);
// insert with right == 0
QMap<int, int>::const_iterator i1 (map.insert(key75, 70, 12)); // overwrite
map.insert(i1, 69, 12); // size 12
sanityCheckTree(map, __LINE__);
QCOMPARE(map.size(), 12);
}
QTEST_APPLESS_MAIN(tst_QMap)
#include "tst_qmap.moc"

View File

@ -64,6 +64,14 @@ private slots:
void iterator_begin();
void ctorStdMap();
void insertion_int_intx();
void insertion_int_int_with_hint1();
void insertion_int_int2();
void insertion_int_int_with_hint2();
void insertion_string_int2();
void insertion_string_int2_hint();
};
@ -76,6 +84,44 @@ void tst_QMap::insertion_int_int()
}
}
void tst_QMap::insertion_int_intx()
{
// This is the same test - but executed later.
// The results in the beginning of the test seems to be a somewhat inaccurate.
QMap<int, int> map;
QBENCHMARK {
for (int i = 0; i < 100000; ++i)
map.insert(i, i);
}
}
void tst_QMap::insertion_int_int_with_hint1()
{
QMap<int, int> map;
QBENCHMARK {
for (int i = 0; i < 100000; ++i)
map.insert(map.constEnd(), i, i);
}
}
void tst_QMap::insertion_int_int2()
{
QMap<int, int> map;
QBENCHMARK {
for (int i = 100000; i >= 0; --i)
map.insert(i, i);
}
}
void tst_QMap::insertion_int_int_with_hint2()
{
QMap<int, int> map;
QBENCHMARK {
for (int i = 100000; i >= 0; --i)
map.insert(map.constBegin(), i, i);
}
}
void tst_QMap::insertion_int_string()
{
QMap<int, QString> map;
@ -203,6 +249,38 @@ void tst_QMap::ctorStdMap()
}
}
class XString : public QString
{
public:
bool operator < (const XString& x) const // an expensive operator <
{
return toInt() < x.toInt();
}
};
void tst_QMap::insertion_string_int2()
{
QMap<XString, int> map;
QBENCHMARK {
for (int i = 1; i < 5000; ++i) {
XString str;
str.setNum(i);
map.insert(str, i);
}
}
}
void tst_QMap::insertion_string_int2_hint()
{
QMap<XString, int> map;
QBENCHMARK {
for (int i = 1; i < 5000; ++i) {
XString str;
str.setNum(i);
map.insert(map.end(), str, i);
}
}
}
QTEST_MAIN(tst_QMap)