【问题标题】:QVector vs QListQVector 与 QList
【发布时间】:2011-09-29 22:19:51
【问题描述】:

我有一个需要迭代的整数列表,但数组不够用。 vectorslists 之间有什么区别?在选择类型之前我需要了解什么?

为了清楚起见,我已经阅读了 QT 文档,但这是我所知道的范围:

QList<T>QLinkedList<T>QVector<T> 提供类似的功能。这是一个概述:

  • 对于大多数用途,QList 是正确使用的类。它的基于索引的 API 比 QLinkedList's 基于迭代器的 API 更方便,并且通常比 QVector 更快,因为它将项目存储在内存中的方式。它还可以扩展为您的可执行文件中的更少代码。
  • 如果您需要一个真正的链表,并保证在列表中间进行恒定时间插入,并且迭代器指向项目而不是索引,请使用 QLinkedList
  • 如果您希望项目占据相邻的内存位置,请使用QVector

【问题讨论】:

    标签: c++ qt list vector


    【解决方案1】:

    QVector 大部分类似于std::vector,正如您可能从名称中猜到的那样。 QList 更接近于 boost::ptr_deque,尽管与 std::list 有明显关联。它不直接存储对象,而是存储指向它们的指针。您可以获得两端快速插入的所有好处,并且重新分配涉及改组指针而不是复制构造函数,但会失去实际std::dequestd::vector 的空间位置,并获得大量堆分配。它确实有一些决策来避免小对象的堆分配,重新获得空间局部性,但据我了解,它仅适用于小于 int 的事物。

    QLinkedList 类似于std::list,并且具有它的所有缺点。一般来说,这应该是您最后选择的容器。

    QT 库非常喜欢使用QList 对象,因此在您自己的代码中使用它们有时可以避免一些不必要的乏味。理论上,在某些情况下,额外的堆使用和实际数据的随机定位可能会造成伤害,但通常不会引起注意。所以我建议使用QList,直到分析建议更改为QVector。如果您希望连续分配很重要[阅读:您正在与需要 T[] 而不是 QList<T> 的代码交互],这也可能是立即使用 QVector 开始的原因。


    如果你问的是一般的容器,并且只是参考了 QT 文档,那么上面的信息就没那么有用了。

    std::vector 是一个可以调整大小的数组。所有元素彼此相邻存储,您可以快速访问各个元素。缺点是插入仅在一端有效。如果你把一些东西放在中间,或者放在开头,你必须复制其他对象来腾出空间。在大哦符号中,在末尾插入是 O(1),在其他任何地方插入是 O(N),随机访问是 O(1)。

    std::deque 类似,但不保证对象彼此相邻存储,并且允许在两端插入 O(1)。它还需要一次分配较小的内存块,这有时很重要。随机访问是 O(1),中间插入是 O(N),与 vector 相同。空间局部性比std::vector 差,但对象往往会聚集在一起,因此您可以获得一些好处。

    std::list 是一个链表。它需要三个标准顺序容器中最多的内存开销,但可以在任何地方快速插入……前提是您提前知道需要插入的位置。它不提供对单个元素的随机访问,因此您必须在 O(N) 中进行迭代。但是一旦到了那里,实际的插入是 O(1)。 std::list 的最大好处是可以快速将它们拼接在一起……如果将整个范围的值移动到不同的 std::list,整个操作是 O(1)。使列表中的引用无效也更加困难,这有时很重要。

    作为一般规则,我更喜欢std::deque 而不是std::vector,除非我需要能够将数据传递到需要原始数组的库。 std::vector 保证是连续的,所以 &v[0] 用于此目的。我不记得我上次使用std::list 是什么时候了,但这几乎可以肯定是因为我需要更强有力的保证来保证引用保持有效。

    【讨论】:

    • FWIW,std::deque 对引用有很好的保证。迭代器很容易失效,但是指向成员的指针对于出队的操作非常健壮。
    • 很好的答案。但我还有一个问题:哪个迭代更快?我有一组对象,它们并不经常插入或删除,但我需要尽可能快地迭代对象。哪个更快?
    • 好消息。我发现以下语句最有用……“QT 库非常支持使用 QList 对象”。在我的用例中,我正在处理一个 QTableWidget,它喜欢 QList 和 QListString。因此,让您的用例决定您在 QVector 和 QList 之间做出的决定。
    • 您是否真的将std::dequestd::vector 进行了基准测试?你会感到惊讶...
    • 请记住,在 Qt 开发人员抱怨 QList 作为默认容器不推荐使用后,该文档已更新。它现在声明:“QVector 应该是您的默认首选。[...] 但是,QList 在整个 Qt API 中用于传递参数和返回值。使用 QList 与这些接口进行交互API。" (QList documentation)
    【解决方案2】:

    情况发生了变化

    我们现在在 Qt 5.8 中,事情发生了变化,所以文档。它对这个问题给出了明确而不同的答案:

    QVector 应该是您的默认首选。 QVector<T> 通常会 提供比QList<T> 更好的性能,因为QVector<T> 总是 将其项目按顺序存储在内存中,QList<T> 将在其中分配 除非sizeof(T) <= sizeof(void*) 和 T 已在堆上 声明为 Q_MOVABLE_TYPEQ_PRIMITIVE_TYPE 使用 Q_DECLARE_TYPEINFO

    查看使用QList 的优缺点 解释。但是,QList 在整个 Qt API 中用于传递 参数和返回值。使用QList 与这些接口 API。

    【讨论】:

    • 这应该上升,现在被接受为答案。
    • 与其他答案中的注释相同:在 Qt 6 中,QVector 已被删除并替换为 QList 的别名,QList 已被 QVector 的实现替换,从而使两个类相同,并且等于旧的 QVector
    【解决方案3】:

    QVector 中类似于std::vectorQLinkedList 类似于 std::listQList 是基于索引的向量,但不保证内存位置(如std::deque)。

    【讨论】:

      【解决方案4】:

      来自 QtList 文档:

      • 在大多数情况下使用 QList。对于具有一千个项目的结构,可以在中间有效插入,并提供索引访问。 prepend()append() 非常快,因为内存是在内部数组的两端预先分配的。 QList<T> 是 T 类型指针的数组。如果 T 有指针或 Qt 类共享指针类型,则对象直接存储在数组中

      • QVector 在大量 append()insert() 的大小大于指针的新项目的情况下是首选,因为 QVector 在单个堆分配中为其项目分配内存。对于QList,插入追加新项需要在堆上分配新项的内存。简而言之,如果您希望项目占据相邻的内存位置,或者如果您的项目大于指针并且您希望避免在插入时将它们单独分配到堆上的开销,那么请使用QVector

      【讨论】:

        【解决方案5】:

        QVector 就像一个可以改变大小(增加或减少)的数组,但它伴随着繁重的事务、计算和时间。

        例如,如果要添加一项,则创建一个新数组,将所有项复制到新数组中,将新项添加到末尾,并删除旧数组。反之亦然删除。

        但是,QLinkedList 可以使用指针。因此,当创建一个新项目时,只会分配一个新的内存空间并链接到唯一的内存块。因为它与指针一起工作,所以它更快更高效。

        如果您有不希望改变大小的项目列表,QVector 可能很好,但通常QLinkedList 用于大多数用途。

        【讨论】:

        • -1:QVector 并没有像您所说的那样处理繁重的事务,因为它预先分配并且具有用于追加和弹出的良好增长/收缩策略。前置/插入确实需要移动数据范围,但是由于它们在内存中是连续的,因此这非常快(缓存可以很好地处理连续数据,这与使用 QList 的分散堆块不同)。您关于 QLinkedList 用于大多数目的的第二个陈述是完全错误的。它很少使用。您可能会将其与 QList 混淆?
        猜你喜欢
        • 2013-11-03
        • 2012-03-24
        • 2016-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多