I just noticed that QList
doesn't have a resize
method, while QVector
, for example, has one. Why is this? And is there an equivalent function?
I think reason is because QList
doesn't require the element type to have a default constructor.
As a result of this, there is no operation where QList
ever creates an object it only copies them.
But if you really need to resize a QList
(for whatever reason), here's a function that will do it. Note that it's just a convenience function, and it's not written with performance in mind.
template<class T>
void resizeList(QList<T> & list, int newSize) {
int diff = newSize - list.size();
T t;
if (diff > 0) {
list.reserve(newSize);
while (diff--) list.append(t);
} else if (diff < 0) list.erase(list.end() + diff, list.end());
}
list.reserve(newSize);
–
Jetpropelled Well, this is the more generic answer, but I hope you will see, by comparising QList
and QVector
why there is no need of manually expanding the container.
QList is using internal buffer to save pointers to the elements (or, if the element is smaller than pointer size, or element is one of the shared classes - elements itself), and the real data will be kept on the heap.
During the time, removing the data will not reduce internal buffer (empty space will be filled by shifting left or right elements, leaving space on the beginning and the end for later insertions).
Appending items, like QVector
will create additional new space on end of the array, and since, unlike QVector
, real data is not stored in internal buffer, you can create a lot of space in single instruction, no matter what size of the item is (unlike QVector
) - because you are simply adding pointers into indexing buffer.
For example, if you are using 32bit system (4 bytes per pointer) and you are storing 50 items in the QList
, and each item is 1MB big, QVector
buffer will need to be resized to 50MB, and QList
's internal buffer is need to allocate only 200B of memory. This is where you need to call resize()
in QVector
, but in QList
there is no need, since allocating small chunk of memory is not problematic, as allocating 50MB of memory.
However, there is a price for that which means that you sometimes you want to preffer QVector
instead of QList
: For single item stored in the QList
, you need one additional alloc on the heap - to keep the real data of the item (data where pointer in the internal buffer is pointing to). If you want to add 10000 items larger than the pointer (because, if it can fit into pointer, it will be stored directly in the internal buffer), you will need 10000 system calls to allocate data for 10000 items on the heap. But, if you are using QVector
, and you call resize
, you are able to fit all the items in the single alloc call - so don't use QList
if you need a lot of inserting or appending, prefer QVector
for that. Of course, if you are using QList
to store shared classes, there is no need for additional allocating, which again makes QList
more suitable.
So, prefer QList
for most of the cases as it is:
- Using indices to access the individual elements, accessing items will be faster that
QLinkedList
- Inserting into middle of the list will only require moving of the pointers to create space, and it is faster than shifting actual
QVector
data around. - There is no need to manually reserve or resize space, as empty space will be moved to the end of the buffer for later use, and allocating space in the array is very fast, as the elements are very small, and it can allocate a lot of space without killing your memory space.
Don't use it in the following scenarios, and prefer QVector
:
- If you need to ensure that your data is stored in the sequential memory locations
- If you are rarely inserting data at the random positions, but you are appending a lot of data at the end or beginning, which can cause a lot of unnecessary system calls, and you still need fast indexing.
- If you are looking for (shared) replacement for simple arrays which will not grow over the time.
And, finally, note: QList
(and QVector
) have reserve(int alloc)
function which will cause QList
's internal buffer to grow if alloc
is greater than the current size of the internal buffer. However, this will not affect external size of the QList
(size()
will always return the exact number of elements contained in the list).
resize
, not as an optimization, but as a means of shrinking or growing the list (and filling in default values if neccessary), because your algorithm/design requires a list of exactly n elements. –
Copyreader resize
in QList
, I think reserve
is a way to go in this case, right? –
Jamila reserve
in turn is a mere optimization, which doesn't really change the outside world's view of the list (the number of elements), whereas resize
is used if you really want the list to change its external size. If there is no resize
for QList
(for the conceptual reasons you described), then you are just unlucky if you need it. Seeing that you propose reserve
as a replacement for resize
, I will put my up-vote in ice until it's fixed. –
Copyreader QVector::resize
will set size()
to return given size, while QList::reserve()
will not affect this. –
Jamila 200KB
for 50 items in the QList case?? –
Colonize I think reason is because QList
doesn't require the element type to have a default constructor.
As a result of this, there is no operation where QList
ever creates an object it only copies them.
But if you really need to resize a QList
(for whatever reason), here's a function that will do it. Note that it's just a convenience function, and it's not written with performance in mind.
template<class T>
void resizeList(QList<T> & list, int newSize) {
int diff = newSize - list.size();
T t;
if (diff > 0) {
list.reserve(newSize);
while (diff--) list.append(t);
} else if (diff < 0) list.erase(list.end() + diff, list.end());
}
list.reserve(newSize);
–
Jetpropelled wasle answer is good, but it'll add the same object multiple time. Here is an utility functions that will add different object for list of smart pointers.
template<class T>
void resizeSmartList(QList<QSharedPointer<T> > & list, int newSize) {
int diff = newSize - list.size();
if (diff > 0) {
list.reserve(diff);
while (diff>0){
QSharedPointer<T> t = QSharedPointer<T>(new T);
list.append(t);
diff--;
}
}else if (diff < 0) list.erase(list.end() + diff, list.end());
}
For use without smart pointers, the following will add different objects to your list.
template<class T>
void resizeList(QList<T> & list, int newSize) {
int diff = newSize - list.size();
if (diff > 0) {
list.reserve(diff);
while (diff>0){
T t = new T;
list.append(t);
diff--;
}
}else if (diff < 0) list.erase(list.end() + diff, list.end());
}
Also remember that your objects must have default constructor (constructor declared in the header with arg="someValue") or else it will fail.
Just use something like
QList<Smth> myList;
// ... some operations on the list here
myList << QVector<Smth>(desiredNewSize - myList.size()).toList();
Essentially, there are these to
/from
Vector
/List
/Set()
methods everywhere, which makes it trivial to resize Qt containers when necessary in a somewhat manual, but trivial and effective (I believe) way.
Another (1 or 2-liner) solution would be:
myList.reserve(newListSize); // note, how we have to reserve manually
std::fill_n(std::back_inserter(myList), desiredNewSize - myList.size(), Smth());
-- that's for STL-oriented folks :)
For some background on how complex an effective QList::resize()
may get, see:
And is there an equivalent function?
, this is quite surprizing, noone (of the 4K viewers) checked out the docs for potentially relevant signatures :) –
Laundryman QList
lacks a resize
method. The answer is: because Qt containers have plenty of problems, and this is just yet another one. –
Lingam © 2022 - 2024 — McMap. All rights reserved.