简体   繁体   English

为什么 QList 没有 resize() 方法?

[英]Why doesn't QList have a resize() method?

I just noticed that QList doesn't have a resize method, while QVector , for example, has one.我只是注意到QList没有resize方法,而QVector ,例如,有一个。 Why is this?为什么是这样? And is there an equivalent function?是否有等效的功能?

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.嗯,这是更通用的答案,但我希望您通过比较QListQVector明白为什么不需要手动扩展容器。

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. QList 使用内部缓冲区来保存指向元素的指针(或者,如果元素小于指针大小,或者元素是共享类之一- 元素本身),真实数据将保存在堆上。

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.附加项目,如QVector将在数组末尾创建额外的新空间,并且由于与QVector不同,真实数据不存储在内部缓冲区中,因此无论项目的大小如何,您都可以在单个指令中创建大量空间(与QVector不同) - 因为您只是将指针添加到索引缓冲区中。

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.例如,如果您使用 32 位系统(每个指针 4 个字节)并且您在QList中存储 50 个项目,并且每个项目有 1MB 大,则QVector缓冲区将需要调整为 50MB,并且需要QList的内部缓冲区只分配 200B 的内存。 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.这是您需要在QVector调用resize()QVector ,但在QList中则不需要,因为分配小块内存没有问题,因为分配 50MB 内存。

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).但是,这样做是有代价的,这意味着您有时想要QVector而不是QList :对于存储在QList中的单个项目,您需要在堆上进行一个额外的分配 - 以保留项目的真实数据(数据在哪里内部缓冲区中的指针指向)。 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.如果要添加大于指针的 10000 个项目(因为,如果它可以装入指针,它将直接存储在内部缓冲区中),您将需要 10000 次系统调用来为堆上的 10000 个项目分配数据。 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.但是,如果您使用QVector并调用resize ,则可以在单个 alloc 调用中容纳所有项目 - 因此,如果您需要大量插入或附加,请不要使用QList ,因此更喜欢QVector Of course, if you are using QList to store shared classes, there is no need for additional allocating, which again makes QList more suitable.当然,如果你使用QList来存储共享类,则不需要额外的分配,这再次使QList更适合。

So, prefer QList for most of the cases as it is:因此,在大多数情况下更喜欢QList ,因为它是:

  1. Using indices to access the individual elements, accessing items will be faster that QLinkedList使用索引访问单个元素,访问项目将比QLinkedList更快
  2. 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.插入列表中间只需要移动指针来创建空间,并且比移动实际QVector数据更快。
  3. 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 :不要在以下场景中使用它,而更喜欢QVector

  1. If you need to ensure that your data is stored in the sequential memory locations如果您需要确保您的数据存储在顺序内存位置
  2. 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.如果你很少在随机位置插入数据,但你在末尾或开头附加了大量数据,这会导致大量不必要的系统调用,那么你仍然需要快速索引。
  3. 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.最后,请注意: QList (和QVector )具有reserve(int alloc)函数,如果alloc大于内部缓冲区的当前大小,它将导致QList的内部缓冲区增长。 However, this will not affect external size of the QList ( size() will always return the exact number of elements contained in the list).但是,这不会影响QList外部大小size()将始终返回列表中包含的元素的确切数量)。

I think reason is because QList doesn't require the element type to have a default constructor.我认为原因是因为QList不需要元素类型具有默认构造函数。 As a result of this, there is no operation where QList ever creates an object it only copies them.因此,没有任何操作可以让QList创建一个只复制它们的对象。

But if you really need to resize a QList (for whatever reason), here's a function that will do it.但是如果你真的需要调整QList大小(无论出于何种原因),这里有一个函数可以做到。 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());
}

wasle answer is good, but it'll add the same object multiple time. wasle 答案很好,但它会多次添加相同的对象。 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.还请记住,您的对象必须具有默认构造函数(在标头中使用 arg="someValue" 声明的构造函数),否则它将失败。

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.本质上,到处都有这些to / from Vector / List / Set()方法,这使得在必要时以某种手动但微不足道且有效(我相信)的方式调整 Qt 容器的大小变得微不足道。

Another (1 or 2-liner) solution would be:另一种(1 或 2 线)解决方案是:

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 :) -- 那是给面向 STL 的人的 :)

For some background on how complex an effective QList::resize() may get, see:有关有效QList::resize()可能变得多么复杂的一些背景,请参阅:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM