Introduce QMap key iterators

So we can have interoperability with algorithms.
Motivated by inefficient code like qDeleteAll(map.keys())

[ChangeLog][QtCore][QMap] Added key iterators, accessible through
keyBegin() and keyEnd().

Change-Id: Ieee2f9ad031e9d1e845a71447746699bbe95b96c
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
This commit is contained in:
Sérgio Martins 2015-07-24 20:30:01 +01:00
parent 75244bf985
commit 07f27fcf6d
4 changed files with 226 additions and 4 deletions

View File

@ -311,3 +311,22 @@ while (i != map.end() && i.key() == "plenty") {
++i;
}
//! [27]
//! [keyiterator1]
for (QMap<int, QString>::const_iterator it = map.cbegin(), end = map.cend(); it != end; ++it) {
cout << "The key: " << it.key() << endl
cout << "The value: " << it.value() << endl;
cout << "Also the value: " << (*it) << endl;
}
//! [keyiterator1]
//! [keyiterator2]
// Inefficient, keys() is expensive
QList<int> keys = map.keys();
int numPrimes = std::count_if(map.cbegin(), map.cend(), isPrimeNumber);
qDeleteAll(map2.keys());
// Efficient, no memory allocation needed
int numPrimes = std::count_if(map.keyBegin(), map.keyEnd(), isPrimeNumber);
qDeleteAll(map2.keyBegin(), map2.keyEnd());
//! [keyiterator2]

View File

@ -858,6 +858,15 @@ void QMapDataBase::freeData(QMapDataBase *d)
\sa begin(), constEnd()
*/
/*! \fn QMap::key_iterator QMap::keyBegin() const
\since 5.6
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first key
in the map.
\sa keyEnd(), firstKey()
*/
/*! \fn QMap::iterator QMap::end()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item
@ -888,6 +897,15 @@ void QMapDataBase::freeData(QMapDataBase *d)
\sa constBegin(), end()
*/
/*! \fn QMap::key_iterator QMap::keyEnd() const
\since 5.6
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
item after the last key in the map.
\sa keyBegin(), lastKey()
*/
/*! \fn const Key &QMap::firstKey() const
\since 5.2
@ -896,7 +914,7 @@ void QMapDataBase::freeData(QMapDataBase *d)
This executes in \l{constant time}.
\sa lastKey(), first(), isEmpty()
\sa lastKey(), first(), keyBegin(), isEmpty()
*/
/*! \fn const Key &QMap::lastKey() const
@ -907,7 +925,7 @@ void QMapDataBase::freeData(QMapDataBase *d)
This executes in \l{logarithmic time}.
\sa firstKey(), last(), isEmpty()
\sa firstKey(), last(), keyEnd(), isEmpty()
*/
/*! \fn T &QMap::first()
@ -1235,7 +1253,7 @@ void QMapDataBase::freeData(QMapDataBase *d)
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
\sa QMap::const_iterator, QMutableMapIterator
\sa QMap::const_iterator, QMap::key_iterator, QMutableMapIterator
*/
/*! \typedef QMap::iterator::difference_type
@ -1458,7 +1476,7 @@ void QMapDataBase::freeData(QMapDataBase *d)
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
\sa QMap::iterator, QMapIterator
\sa QMap::iterator, QMap::key_iterator, QMapIterator
*/
/*! \typedef QMap::const_iterator::difference_type
@ -1634,6 +1652,134 @@ void QMapDataBase::freeData(QMapDataBase *d)
\sa operator+=(), operator-()
*/
/*! \class QMap::key_iterator
\inmodule QtCore
\since 5.6
\brief The QMap::key_iterator class provides an STL-style const iterator for QMap and QMultiMap keys.
QMap::key_iterator is essentially the same as QMap::const_iterator
with the difference that operator*() and operator->() return a key
instead of a value.
For most uses QMap::iterator and QMap::const_iterator should be used,
you can easily access the key by calling QMap::iterator::key():
\snippet code/src_corelib_tools_qmap.cpp keyiterator1
However, to have interoperability between QMap's keys and STL-style
algorithms we need an iterator that dereferences to a key instead
of a value. With QMap::key_iterator we can apply an algorithm to a
range of keys without having to call QMap::keys(), which is inefficient
as it costs one QMap iteration and memory allocation to create a temporary
QList.
\snippet code/src_corelib_tools_qmap.cpp keyiterator2
QMap::key_iterator is const, it's not possible to modify the key.
The default QMap::key_iterator constructor creates an uninitialized
iterator. You must initialize it using a QMap function like
QMap::keyBegin() or QMap::keyEnd().
\warning Iterators on implicitly shared containers do not work
exactly like STL-iterators. You should avoid copying a container
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
\sa QMap::const_iterator, QMap::iterator
*/
/*! \typedef QMap::key_iterator::difference_type
\internal
*/
/*! \typedef QMap::key_iterator::iterator_category
\internal
*/
/*! \typedef QMap::key_iterator::pointer
\internal
*/
/*! \typedef QMap::key_iterator::reference
\internal
*/
/*! \typedef QMap::key_iterator::value_type
\internal
*/
/*! \fn const T &QMap::key_iterator::operator*() const
Returns the current item's key.
*/
/*! \fn const T *QMap::key_iterator::operator->() const
Returns a pointer to the current item's key.
*/
/*! \fn bool QMap::key_iterator::operator==(key_iterator other)
Returns \c true if \a other points to the same item as this
iterator; otherwise returns \c false.
\sa operator!=()
*/
/*! \fn bool QMap::key_iterator::operator!=(key_iterator other)
Returns \c true if \a other points to a different item than this
iterator; otherwise returns \c false.
\sa operator==()
*/
/*!
\fn QMap::key_iterator &QMap::key_iterator::operator++()
The prefix ++ operator (\c{++i}) advances the iterator to the
next item in the hash and returns an iterator to the new current
item.
Calling this function on QMap::keyEnd() leads to undefined results.
\sa operator--()
*/
/*! \fn QMap::key_iterator QMap::key_iterator::operator++(int)
\overload
The postfix ++ operator (\c{i++}) advances the iterator to the
next item in the hash and returns an iterator to the previous
item.
*/
/*! \fn QMap::key_iterator &QMap::key_iterator::operator--()
The prefix -- operator (\c{--i}) makes the preceding item
current and returns an iterator pointing to the new current item.
Calling this function on QMap::keyBegin() leads to undefined
results.
\sa operator++()
*/
/*! \fn QMap::key_iterator QMap::key_iterator::operator--(int)
\overload
The postfix -- operator (\c{i--}) makes the preceding item
current and returns an iterator pointing to the previous
item.
*/
/*! \fn const_iterator QMap::key_iterator::base() const
Returns the underlying const_iterator this key_iterator is based on.
*/
/*! \fn QDataStream &operator<<(QDataStream &out, const QMap<Key, T> &map)
\relates QMap

View File

@ -518,6 +518,32 @@ public:
};
friend class const_iterator;
class key_iterator
{
const_iterator i;
public:
typedef typename const_iterator::iterator_category iterator_category;
typedef typename const_iterator::difference_type difference_type;
typedef Key value_type;
typedef const Key *pointer;
typedef const Key &reference;
explicit key_iterator(const_iterator o) : i(o) { }
const Key &operator*() const { return i.key(); }
const Key *operator->() const { return &i.key(); }
bool operator==(key_iterator o) const { return i == o.i; }
bool operator!=(key_iterator o) const { return i != o.i; }
inline key_iterator &operator++() { ++i; return *this; }
inline key_iterator operator++(int) { return key_iterator(i++);}
inline key_iterator &operator--() { --i; return *this; }
inline key_iterator operator--(int) { return key_iterator(i--); }
const_iterator base() const { return i; }
};
// STL style
inline iterator begin() { detach(); return iterator(d->begin()); }
inline const_iterator begin() const { return const_iterator(d->begin()); }
@ -527,6 +553,8 @@ public:
inline const_iterator end() const { return const_iterator(d->end()); }
inline const_iterator constEnd() const { return const_iterator(d->end()); }
inline const_iterator cend() const { return const_iterator(d->end()); }
inline key_iterator keyBegin() const { return key_iterator(begin()); }
inline key_iterator keyEnd() const { return key_iterator(end()); }
iterator erase(iterator it);
// more Qt

View File

@ -65,6 +65,7 @@ private slots:
void take();
void iterators();
void keyIterator();
void keys_values_uniqueKeys();
void qmultimap_specific();
@ -835,6 +836,34 @@ void tst_QMap::iterators()
}
}
void tst_QMap::keyIterator()
{
QMap<int, int> map;
for (int i = 0; i < 100; ++i)
map.insert(i, i*100);
QMap<int, int>::key_iterator key_it = map.keyBegin();
QMap<int, int>::const_iterator it = map.cbegin();
for (int i = 0; i < 100; ++i) {
QCOMPARE(*key_it, it.key());
++key_it;
++it;
}
key_it = std::find(map.keyBegin(), map.keyEnd(), 50);
it = std::find(map.cbegin(), map.cend(), 50 * 100);
QVERIFY(key_it != map.keyEnd());
QCOMPARE(*key_it, it.key());
QCOMPARE(*(key_it++), (it++).key());
QCOMPARE(*(key_it--), (it--).key());
QCOMPARE(*(++key_it), (++it).key());
QCOMPARE(*(--key_it), (--it).key());
QCOMPARE(std::count(map.keyBegin(), map.keyEnd(), 99), 1);
}
void tst_QMap::keys_values_uniqueKeys()
{
QMap<QString, int> map;