From 4ec6748c6a30f74e6d8fbb90fc118b306d1fa690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Tue, 26 Nov 2019 16:08:38 +0100 Subject: QMap: deprecate insertMulti, unite and friends insertMulti and unite will silently transform a QMap into a multi-map which is not behavior we want to keep around anymore and as such is being deprecated. QMap functions that only make sense in a multi-map scenario are also deprecated and the implementation is moved to QMultiMap where it makes sense. Use QMultiMap if multiple keys are desired and insert(const QMap &) if a non multi-map-converting unite is desired. [ChangeLog][QtCore][QMap] insertMulti(), unite(), values(Key), uniqueKeys(), count(Key) is now deprecated. Please use QMultiMap instead. Task-number: QTBUG-35544 Change-Id: I158c938ceefb5aaba0e6e7513b2c26a64d29d521 Reviewed-by: Qt CI Bot Reviewed-by: Lars Knoll --- src/corelib/tools/qmap.cpp | 95 +++++++----- src/corelib/tools/qmap.h | 351 ++++++++++++++++++++++++++------------------- 2 files changed, 262 insertions(+), 184 deletions(-) (limited to 'src/corelib') diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 970373101f..d747a8cda4 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -468,10 +468,9 @@ void QMapDataBase::freeData(QMapDataBase *d) \snippet code/src_corelib_tools_qmap.cpp 9 However, you can store multiple values per key by using - insertMulti() instead of insert() (or using the convenience - subclass QMultiMap). If you want to retrieve all the values for a - single key, you can use values(const Key &key), which returns a - QList: + using the subclass QMultiMap. If you want + to retrieve all the values for a single key, you can use + values(const Key &key), which returns a QList: \snippet code/src_corelib_tools_qmap.cpp 10 @@ -676,9 +675,8 @@ void QMapDataBase::freeData(QMapDataBase *d) /*! \fn template int QMap::remove(const Key &key) Removes all the items that have the key \a key from the map. - Returns the number of items removed which is usually 1 but will be - 0 if the key isn't in the map, or \> 1 if insertMulti() has been - used with the \a key. + Returns the number of items removed which will be 1 if the key + exists in the map, and 0 otherwise. \sa clear(), take(), QMultiMap::remove() */ @@ -742,28 +740,26 @@ void QMapDataBase::freeData(QMapDataBase *d) /*! \fn template QList QMap::uniqueKeys() const \since 4.2 + \obsolete Returns a list containing all the keys in the map in ascending order. Keys that occur multiple times in the map (because items were inserted with insertMulti(), or unite() was used) occur only once in the returned list. - \sa keys(), values() + \sa QMultiMap::uniqueKeys() */ /*! \fn template QList QMap::keys() const Returns a list containing all the keys in the map in ascending - order. Keys that occur multiple times in the map (because items - were inserted with insertMulti(), or unite() was used) also - occur multiple times in the list. - - To obtain a list of unique keys, where each key from the map only - occurs once, use uniqueKeys(). + order. Keys that occur multiple times in the map (because the + method is operating on a QMultiMap) also occur multiple times + in the list. The order is guaranteed to be the same as that used by values(). - \sa uniqueKeys(), values(), key() + \sa QMultiMap::uniqueKeys(), values(), key() */ /*! \fn template QList QMap::keys(const T &value) const @@ -806,6 +802,7 @@ void QMapDataBase::freeData(QMapDataBase *d) */ /*! \fn template QList QMap::values(const Key &key) const + \obsolete \overload @@ -813,14 +810,15 @@ void QMapDataBase::freeData(QMapDataBase *d) \a key, from the most recently inserted to the least recently inserted one. - \sa count(), insertMulti() + \sa QMultiMap::values() */ /*! \fn template int QMap::count(const Key &key) const + \obsolete Returns the number of items associated with key \a key. - \sa contains(), insertMulti(), QMultiMap::count() + \sa QMultiMap::count() */ /*! \fn template int QMap::count() const @@ -1118,7 +1116,7 @@ void QMapDataBase::freeData(QMapDataBase *d) If there are multiple items with the key \a key, the most recently inserted item's value is replaced with \a value. - \sa insertMulti() + \sa QMultiMap::insert() */ /*! \fn template QMap::iterator QMap::insert(const_iterator pos, const Key &key, const T &value) @@ -1147,7 +1145,7 @@ void QMapDataBase::freeData(QMapDataBase *d) \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() + \sa QMultiMap::insert() */ /*! \fn template void QMap::insert(const QMap &map) @@ -1161,10 +1159,11 @@ void QMapDataBase::freeData(QMapDataBase *d) \note If \a map contains multiple entries with the same key then the final value of the key is undefined. - \sa insertMulti() + \sa QMultiMap::insert() */ /*! \fn template QMap::iterator QMap::insertMulti(const Key &key, const T &value) + \obsolete Inserts a new item with the key \a key and a value of \a value. @@ -1173,12 +1172,13 @@ void QMapDataBase::freeData(QMapDataBase *d) different from insert(), which overwrites the value of an existing item.) - \sa insert(), values() + \sa QMultiMap::insert() */ /*! \fn template QMap::iterator QMap::insertMulti(const_iterator pos, const Key &key, const T &value) \overload \since 5.1 + \obsolete Inserts a new item with the key \a key and value \a value and with hint \a pos suggesting where to do the insert. @@ -1192,17 +1192,18 @@ void QMapDataBase::freeData(QMapDataBase *d) \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 insert() + \sa QMultiMap::insert() */ /*! \fn template QMap &QMap::unite(const QMap &other) + \obsolete Inserts all the items in the \a other map into this map. If a key is common to both maps, the resulting map will contain the key multiple times. - \sa insertMulti() + \sa QMultiMap::unite() */ /*! \typedef QMap::Iterator @@ -1285,9 +1286,8 @@ void QMapDataBase::freeData(QMapDataBase *d) Unlike QHash, which stores its items in an arbitrary order, QMap stores its items ordered by key. Items that share the same key - (because they were inserted using QMap::insertMulti(), or due to a - unite()) will appear consecutively, from the most recently to the - least recently inserted value. + (because the map is a QMultiMap) will appear consecutively, + from the most recently to the least recently inserted value. Let's see a few examples of things we can do with a QMap::iterator that we cannot do with a QMap::const_iterator. @@ -1533,9 +1533,8 @@ void QMapDataBase::freeData(QMapDataBase *d) Unlike QHash, which stores its items in an arbitrary order, QMap stores its items ordered by key. Items that share the same key - (because they were inserted using QMap::insertMulti()) will - appear consecutively, from the most recently to the least - recently inserted value. + (because the map is a QMultiMap) will appear consecutively, + from the most recently to the least recently inserted value. Multiple iterators can be used on the same map. If you add items to the map, existing iterators will remain valid. If you remove @@ -1907,20 +1906,20 @@ void QMapDataBase::freeData(QMapDataBase *d) \reentrant QMultiMap\ is one of Qt's generic \l{container classes}. - It inherits QMap and extends it with a few convenience functions - that make it more suitable than QMap for storing multi-valued - maps. A multi-valued map is a map that allows multiple values - with the same key; QMap normally doesn't allow that, unless you - call QMap::insertMulti(). + It inherits QMap and extends it with a few functions + that make it able to store multi-valued maps. A multi-valued map + is a map that allows multiple values with the same key; QMap + doesn't allow that. Because QMultiMap inherits QMap, all of QMap's functionality also applies to QMultiMap. For example, you can use isEmpty() to test whether the map is empty, and you can traverse a QMultiMap using QMap's iterator classes (for example, QMapIterator). But in - addition, it provides an insert() function that corresponds to - QMap::insertMulti(), and a replace() function that corresponds to - QMap::insert(). It also provides convenient operator+() and - operator+=(). + addition, it provides an insert() function that inserts but does + not overwrite any previous value if the key already exists, + and a replace() function that corresponds which does overwite + an existing value if they key is already in the map. + It also provides convenient operator+() and operator+=(). Example: \snippet code/src_corelib_tools_qmap.cpp 25 @@ -2109,4 +2108,24 @@ void QMapDataBase::freeData(QMapDataBase *d) \sa QMap::constFind() */ +/*! \fn template QList QMultiMap::values(const Key &key) const + + Returns a list containing all the values associated with key + \a key, from the most recently inserted to the least recently + inserted one. +*/ + +/*! \fn template int QMultiMap::count(const Key &key) const + + Returns the number of items associated with key \a key. +*/ + +/*! \fn template QList QMultiMap::uniqueKeys() const + \since 4.2 + + Returns a list containing all the keys in the map in ascending + order. Keys that occur multiple times in the map occur only + once in the returned list. +*/ + QT_END_NAMESPACE diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index fa736e8413..281812b5e6 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -380,12 +380,15 @@ public: T &operator[](const Key &key); const T operator[](const Key &key) const; - QList uniqueKeys() const; QList keys() const; QList keys(const T &value) const; QList values() const; - QList values(const Key &key) const; - int count(const Key &key) const; +#if QT_DEPRECATED_SINCE(5, 15) + QT_DEPRECATED_X("Use QMultiMap for maps storing multiple values with the same key.") QList uniqueKeys() const; + QT_DEPRECATED_X("Use QMultiMap for maps storing multiple values with the same key.") QList values(const Key &key) const; + QT_DEPRECATED_X("Use QMultiMap for maps storing multiple values with the same key.") int count(const Key &key) const; +#endif + inline const Key &firstKey() const { Q_ASSERT(!isEmpty()); return constBegin().key(); } inline const Key &lastKey() const { Q_ASSERT(!isEmpty()); return (constEnd() - 1).key(); } @@ -452,6 +455,7 @@ public: { return i != o.i; } #endif friend class QMap; + friend class QMultiMap; }; friend class iterator; @@ -514,6 +518,7 @@ public: inline bool operator!=(const iterator &o) const { return operator!=(const_iterator(o)); } #endif friend class QMap; + friend class QMultiMap; }; friend class const_iterator; @@ -579,9 +584,11 @@ public: iterator insert(const Key &key, const T &value); iterator insert(const_iterator pos, const Key &key, const T &value); void insert(const QMap &map); - iterator insertMulti(const Key &key, const T &value); - iterator insertMulti(const_iterator pos, const Key &akey, const T &avalue); - QMap &unite(const QMap &other); +#if QT_DEPRECATED_SINCE(5, 15) + QT_DEPRECATED_X("Use QMultiMap for maps storing multiple values with the same key.") iterator insertMulti(const Key &key, const T &value); + QT_DEPRECATED_X("Use QMultiMap for maps storing multiple values with the same key.") iterator insertMulti(const_iterator pos, const Key &akey, const T &avalue); + QT_DEPRECATED_X("Use QMultiMap for maps storing multiple values with the same key.") QMap &unite(const QMap &other); +#endif // STL compatibility typedef Key key_type; @@ -610,6 +617,8 @@ private: return true; #endif } + + friend class QMultiMap; }; template @@ -671,23 +680,6 @@ Q_INLINE_TEMPLATE T &QMap::operator[](const Key &akey) return n->value; } -template -Q_INLINE_TEMPLATE int QMap::count(const Key &akey) const -{ - Node *firstNode; - Node *lastNode; - d->nodeRange(akey, &firstNode, &lastNode); - - const_iterator ci_first(firstNode); - const const_iterator ci_last(lastNode); - int cnt = 0; - while (ci_first != ci_last) { - ++cnt; - ++ci_first; - } - return cnt; -} - template Q_INLINE_TEMPLATE bool QMap::contains(const Key &akey) const { @@ -832,75 +824,6 @@ Q_INLINE_TEMPLATE void QMap::insert(const QMap &map) } } -template -Q_INLINE_TEMPLATE typename QMap::iterator QMap::insertMulti(const Key &akey, - const T &avalue) -{ - detach(); - Node* y = d->end(); - Node* x = static_cast(d->root()); - bool left = true; - while (x != nullptr) { - left = !qMapLessThanKey(x->key, akey); - y = x; - x = left ? x->leftNode() : x->rightNode(); - } - Node *z = d->createNode(akey, avalue, y, left); - return iterator(z); -} - -template -typename QMap::iterator QMap::insertMulti(const_iterator pos, const Key &akey, const T &avalue) -{ - if (d->ref.isShared()) - return this->insertMulti(akey, avalue); - - Q_ASSERT_X(isValidIterator(pos), "QMap::insertMulti", "The specified const_iterator argument 'pos' is invalid"); - - if (pos == constEnd()) { - // Hint is that the Node is larger than (or equal to) the largest value. - Node *n = static_cast(pos.i->left); - if (n) { - while (n->right) - n = static_cast(n->right); - - if (!qMapLessThanKey(n->key, akey)) - return this->insertMulti(akey, avalue); // ignore hint - Node *z = d->createNode(akey, avalue, n, false); // insert right most - return iterator(z); - } - return this->insertMulti(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(pos.i); - if (qMapLessThanKey(next->key, akey)) - return this->insertMulti(akey, avalue); // ignore hint - - if (pos == constBegin()) { - // There is no previous value (insert left most) - Node *z = d->createNode(akey, avalue, begin().i, true); - return iterator(z); - } else { - Node *prev = const_cast(pos.i->previousNode()); - if (!qMapLessThanKey(prev->key, akey)) - return this->insertMulti(akey, avalue); // ignore hint - - // Hint is ok - do insert - if (prev->right == nullptr) { - Node *z = d->createNode(akey, avalue, prev, false); - return iterator(z); - } - if (next->left == nullptr) { - Node *z = d->createNode(akey, avalue, next, true); - return iterator(z); - } - Q_ASSERT(false); // We should have prev->right == nullptr or next->left == nullptr. - return this->insertMulti(akey, avalue); - } - } -} - template Q_INLINE_TEMPLATE typename QMap::const_iterator QMap::constFind(const Key &akey) const @@ -923,19 +846,6 @@ Q_INLINE_TEMPLATE typename QMap::iterator QMap::find(const Key & return iterator(n ? n : d->end()); } -template -Q_INLINE_TEMPLATE QMap &QMap::unite(const QMap &other) -{ - QMap copy(other); - const_iterator it = copy.constEnd(); - const const_iterator b = copy.constBegin(); - while (it != b) { - --it; - insertMulti(it.key(), it.value()); - } - return *this; -} - template QPair::iterator, typename QMap::iterator> QMap::equal_range(const Key &akey) { @@ -1051,26 +961,6 @@ Q_OUTOFLINE_TEMPLATE void QMap::detach_helper() d->recalcMostLeftNode(); } -template -Q_OUTOFLINE_TEMPLATE QList QMap::uniqueKeys() const -{ - QList res; - res.reserve(size()); // May be too much, but assume short lifetime - const_iterator i = begin(); - if (i != end()) { - for (;;) { - const Key &aKey = i.key(); - res.append(aKey); - do { - if (++i == end()) - goto break_out_of_outer_loop; - } while (!qMapLessThanKey(aKey, i.key())); // loop while (key == i.key()) - } - } -break_out_of_outer_loop: - return res; -} - template Q_OUTOFLINE_TEMPLATE QList QMap::keys() const { @@ -1123,21 +1013,6 @@ Q_OUTOFLINE_TEMPLATE QList QMap::values() const return res; } -template -Q_OUTOFLINE_TEMPLATE QList QMap::values(const Key &akey) const -{ - QList res; - Node *n = d->findNode(akey); - if (n) { - const_iterator it(n); - do { - res.append(*it); - ++it; - } while (it != constEnd() && !qMapLessThanKey(akey, it.key())); - } - return res; -} - template Q_INLINE_TEMPLATE typename QMap::const_iterator QMap::lowerBound(const Key &akey) const { @@ -1234,15 +1109,20 @@ public: QMultiMap(QMap &&other) noexcept : QMap(std::move(other)) {} void swap(QMultiMap &other) noexcept { QMap::swap(other); } + QList uniqueKeys() const; + QList values(const Key &key) const; + + using typename QMap::iterator; + using typename QMap::const_iterator; + inline typename QMap::iterator replace(const Key &key, const T &value) { return QMap::insert(key, value); } - inline typename QMap::iterator insert(const Key &key, const T &value) - { return QMap::insertMulti(key, value); } - inline typename QMap::iterator insert(typename QMap::const_iterator pos, const Key &key, const T &value) - { return QMap::insertMulti(pos, key, value); } + iterator insert(const Key &key, const T &value); + iterator insert(const_iterator pos, const Key &key, const T &value); + QMultiMap &unite(const QMultiMap &other); inline QMultiMap &operator+=(const QMultiMap &other) - { this->unite(other); return *this; } + { return unite(other); } inline QMultiMap operator+(const QMultiMap &other) const { QMultiMap result = *this; result += other; return result; } @@ -1251,11 +1131,18 @@ public: using QMap::count; using QMap::find; using QMap::constFind; + using QMap::values; + using QMap::size; + using QMap::detach; + using QMap::erase; + using QMap::isValidIterator; + using typename QMap::Node; bool contains(const Key &key, const T &value) const; int remove(const Key &key, const T &value); + int count(const Key &key) const; int count(const Key &key, const T &value) const; typename QMap::iterator find(const Key &key, const T &value) { @@ -1285,6 +1172,123 @@ private: const T operator[](const Key &key) const; }; +template +Q_OUTOFLINE_TEMPLATE QList QMultiMap::uniqueKeys() const +{ + QList res; + res.reserve(size()); // May be too much, but assume short lifetime + const_iterator i = this->begin(); + if (i != this->end()) { + for (;;) { + const Key &aKey = i.key(); + res.append(aKey); + do { + if (++i == this->end()) + goto break_out_of_outer_loop; + } while (!qMapLessThanKey(aKey, i.key())); // loop while (key == i.key()) + } + } +break_out_of_outer_loop: + return res; +} + +template +Q_OUTOFLINE_TEMPLATE QList QMultiMap::values(const Key &akey) const +{ + QList res; + Node *n = this->d->findNode(akey); + if (n) { + const_iterator it(n); + do { + res.append(*it); + ++it; + } while (it != this->constEnd() && !qMapLessThanKey(akey, it.key())); + } + return res; +} + +template +Q_INLINE_TEMPLATE typename QMultiMap::iterator QMultiMap::insert(const Key &akey, + const T &avalue) +{ + detach(); + Node* y = this->d->end(); + Node* x = static_cast(this->d->root()); + bool left = true; + while (x != nullptr) { + left = !qMapLessThanKey(x->key, akey); + y = x; + x = left ? x->leftNode() : x->rightNode(); + } + Node *z = this->d->createNode(akey, avalue, y, left); + return iterator(z); +} + +template +typename QMultiMap::iterator QMultiMap::insert(const_iterator pos, const Key &akey, const T &avalue) +{ + if (this->d->ref.isShared()) + return insert(akey, avalue); + + Q_ASSERT_X(isValidIterator(pos), "QMap::insert", "The specified const_iterator argument 'pos' is invalid"); + + if (pos == this->constEnd()) { + // Hint is that the Node is larger than (or equal to) the largest value. + Node *n = static_cast(pos.i->left); + if (n) { + while (n->right) + n = static_cast(n->right); + + if (!qMapLessThanKey(n->key, akey)) + return insert(akey, avalue); // ignore hint + Node *z = this->d->createNode(akey, avalue, n, false); // insert right most + return iterator(z); + } + return 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(pos.i); + if (qMapLessThanKey(next->key, akey)) + return insert(akey, avalue); // ignore hint + + if (pos == this->constBegin()) { + // There is no previous value (insert left most) + Node *z = this->d->createNode(akey, avalue, this->begin().i, true); + return iterator(z); + } else { + Node *prev = const_cast(pos.i->previousNode()); + if (!qMapLessThanKey(prev->key, akey)) + return insert(akey, avalue); // ignore hint + + // Hint is ok - do insert + if (prev->right == nullptr) { + Node *z = this->d->createNode(akey, avalue, prev, false); + return iterator(z); + } + if (next->left == nullptr) { + Node *z = this->d->createNode(akey, avalue, next, true); + return iterator(z); + } + Q_ASSERT(false); // We should have prev->right == nullptr or next->left == nullptr. + return insert(akey, avalue); + } + } +} + +template +Q_INLINE_TEMPLATE QMultiMap &QMultiMap::unite(const QMultiMap &other) +{ + QMultiMap copy(other); + const_iterator it = copy.constEnd(); + const const_iterator b = copy.constBegin(); + while (it != b) { + --it; + insert(it.key(), it.value()); + } + return *this; +} + template Q_INLINE_TEMPLATE bool QMultiMap::contains(const Key &key, const T &value) const { @@ -1299,7 +1303,7 @@ Q_INLINE_TEMPLATE int QMultiMap::remove(const Key &key, const T &value) typename QMap::iterator end(QMap::end()); while (i != end && !qMapLessThanKey(key, i.key())) { if (i.value() == value) { - i = this->erase(i); + i = erase(i); ++n; } else { ++i; @@ -1308,6 +1312,23 @@ Q_INLINE_TEMPLATE int QMultiMap::remove(const Key &key, const T &value) return n; } +template +Q_INLINE_TEMPLATE int QMultiMap::count(const Key &akey) const +{ + QMultiMap::Node *firstNode; + QMultiMap::Node *lastNode; + this->d->nodeRange(akey, &firstNode, &lastNode); + + const_iterator ci_first(firstNode); + const const_iterator ci_last(lastNode); + int cnt = 0; + while (ci_first != ci_last) { + ++cnt; + ++ci_first; + } + return cnt; +} + template Q_INLINE_TEMPLATE int QMultiMap::count(const Key &key, const T &value) const { @@ -1322,6 +1343,44 @@ Q_INLINE_TEMPLATE int QMultiMap::count(const Key &key, const T &value) c return n; } +#if QT_DEPRECATED_SINCE(5, 15) +template +QList QMap::uniqueKeys() const +{ + return static_cast *>(this)->uniqueKeys(); +} + +template +QList QMap::values(const Key &key) const +{ + return static_cast *>(this)->values(key); +} + +template +int QMap::count(const Key &key) const +{ + return static_cast *>(this)->count(key); +} + +template +typename QMap::iterator QMap::insertMulti(const Key &key, const T &value) +{ + return static_cast *>(this)->insert(key, value); +} + +template +typename QMap::iterator QMap::insertMulti(const_iterator pos, const Key &akey, const T &avalue) +{ + return static_cast *>(this)->insert(pos, akey, avalue); +} + +template +QMap &QMap::unite(const QMap &other) +{ + return static_cast *>(this)->unite(other); +} +#endif + Q_DECLARE_ASSOCIATIVE_ITERATOR(Map) Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Map) -- cgit v1.2.3