QVector so với QList


80

Tôi có một danh sách các số nguyên mà tôi cần phải lặp lại nhưng một mảng không đủ. Sự khác biệt giữa vectorslistsvà có điều gì tôi cần biết trước khi chọn một loại không?

Nói rõ hơn, tôi đã đọc tài liệu QT nhưng đây là phạm vi của những gì tôi biết:

QList<T>, QLinkedList<T>QVector<T>cung cấp chức năng tương tự. Đây là tổng quan:

  • Đối với hầu hết các mục đích, QListlà lớp phù hợp để sử dụng. API dựa trên chỉ mục của nó thuận tiện hơn QLinkedList'sAPI dựa trên trình lặp và nó thường nhanh hơn QVectorvì cách nó lưu trữ các mục của mình trong bộ nhớ. Nó cũng mở rộng thành ít mã hơn trong tệp thực thi của bạn.
  • Nếu bạn cần một danh sách được liên kết thực sự, với sự đảm bảo về thời gian liên tục chèn vào giữa danh sách và trình vòng lặp cho các mục thay vì chỉ mục, hãy sử dụng QLinkedList.
  • Nếu bạn muốn các mục chiếm các vị trí bộ nhớ liền kề, hãy sử dụng QVector.

Câu trả lời:


122

QVectorchủ yếu là tương tự std::vector, như bạn có thể đoán từ tên. QListlà gần hơn boost::ptr_deque, mặc dù liên kết rõ ràng với std::list. Nó không lưu trữ trực tiếp các đối tượng mà thay vào đó, nó lưu trữ các con trỏ tới chúng. Bạn đạt được tất cả những lợi ích của việc chèn nhanh ở cả hai đầu và việc phân bổ lại liên quan đến việc xáo trộn con trỏ thay vì sao chép các hàm tạo, nhưng làm mất vị trí không gian của một thực tế std::dequehoặc std::vectorvà đạt được nhiều phân bổ đống. Nó thực sự có một số quyết định để tránh phân bổ đống cho các đối tượng nhỏ, lấy lại vị trí không gian, nhưng từ những gì tôi hiểu, nó chỉ áp dụng cho những thứ nhỏ hơn an int.

QLinkedListtương tự std::listvà có tất cả các mặt trái của nó. Nói chung, đây sẽ là lựa chọn cuối cùng của bạn về một container.

Thư viện QT rất ủng hộ việc sử dụng các QListđối tượng, vì vậy việc ưu tiên chúng trong mã của riêng bạn đôi khi có thể tránh được một số tẻ nhạt không cần thiết. Việc sử dụng thêm đống và định vị ngẫu nhiên của dữ liệu thực tế về mặt lý thuyết có thể gây hại trong một số trường hợp, nhưng đôi khi là không đáng chú ý. Vì vậy, tôi sẽ đề nghị sử dụng QListcho đến khi cấu hình gợi ý thay đổi thành a QVector. Nếu bạn mong đợi phân bổ liền kề là quan trọng [đọc: bạn đang giao tiếp với mã mong đợi một T[]thay vì a QList<T>] thì đó cũng có thể là một lý do để bắt đầu QVectorngay từ đầu.


Nếu bạn đang hỏi về container nói chung và chỉ sử dụng các tài liệu QT để tham khảo, thì thông tin trên ít hữu ích.

An std::vectorlà một mảng mà bạn có thể thay đổi kích thước. Tất cả các phần tử được lưu trữ bên cạnh nhau và bạn có thể truy cập các phần tử riêng lẻ một cách nhanh chóng. Nhược điểm là các chèn chỉ hiệu quả ở một đầu. Nếu bạn đặt thứ gì đó ở giữa, hoặc ở đầu, bạn phải sao chép những đồ vật khác để có chỗ. Trong ký hiệu big-oh, phần chèn ở cuối là O (1), phần chèn ở bất kỳ nơi nào khác là O (N) và truy cập ngẫu nhiên là O (1).

An std::dequetương tự, nhưng không lưu trữ các đối tượng guarentee cạnh nhau và cho phép chèn ở cả hai đầu là O (1). Nó cũng yêu cầu phân bổ các phần bộ nhớ nhỏ hơn tại một thời điểm, điều này đôi khi có thể quan trọng. Truy cập ngẫu nhiên là O (1) và nội tiếp ở giữa là O (N), tương tự như đối với a vector. Địa phương không gian kém hơn std::vector, nhưng các đối tượng có xu hướng được tập hợp lại để bạn đạt được một số lợi ích.

An std::listlà một danh sách liên kết. Nó yêu cầu nhiều bộ nhớ nhất trong ba vùng chứa tuần tự tiêu chuẩn, nhưng cung cấp khả năng chèn nhanh ở mọi nơi ... miễn là bạn biết trước nơi bạn cần chèn. Nó không cung cấp quyền truy cập ngẫu nhiên vào các phần tử riêng lẻ, vì vậy bạn phải lặp lại trong O (N). Nhưng khi ở đó, phần chèn thực tế là O (1). Lợi ích lớn nhất std::listlà bạn có thể nối chúng với nhau một cách nhanh chóng ... nếu bạn di chuyển toàn bộ phạm vi giá trị sang một giá trị khác std::list, toàn bộ hoạt động là O (1). Việc vô hiệu hóa các tham chiếu trong danh sách cũng khó hơn nhiều, điều này đôi khi có thể quan trọng.

Theo nguyên tắc chung, tôi thích std::dequelàm như vậy std::vector, trừ khi tôi cần có thể chuyển dữ liệu tới một thư viện mong đợi một mảng thô. std::vectorđược đảm bảo liền kề, vì vậy &v[0]hoạt động cho mục đích này. Tôi không nhớ lần cuối cùng tôi sử dụng a std::list, nhưng gần như chắc chắn là do tôi cần người có thẩm quyền mạnh hơn về các tham chiếu vẫn còn giá trị.


FWIW, std :: deque có những đảm bảo khá tốt về tài liệu tham khảo. Các trình lặp lại dễ dàng bị vô hiệu hóa, nhưng các con trỏ đến các thành viên khá mạnh đối với các hoạt động trên dequeue.
Ben

Câu trả lời chính xác. Nhưng tôi có một câu hỏi bổ sung: Lặp lại cái nào nhanh hơn? Tôi có một tập hợp các đối tượng, chúng không thực sự được chèn hoặc loại bỏ thường xuyên, nhưng tôi cần lặp lại các đối tượng càng nhanh càng tốt. Cái nào nhanh hơn?
birgersp

Thông tin tốt. Tôi thấy câu sau đây hữu ích nhất ... "Thư viện QT rất ủng hộ việc sử dụng các đối tượng QList". Trong trường hợp sử dụng của tôi, tôi đang xử lý một QTableWidget, giống như QList và QListString. Do đó, hãy để trường hợp sử dụng của bạn quyết định quyết định của bạn giữa QVector và QList.
cá biển

1
Bạn đã acually benchmarked std::dequechống lại std::vector? Bạn sẽ ngạc nhiên ...
Marc Mutz - mmutz

4
Xin lưu ý rằng tài liệu đã được cập nhật sau những phàn nàn từ các nhà phát triển Qt rằng QList là một đề xuất kém như một vùng chứa mặc định. Bây giờ nó tuyên bố: " QVector nên là lựa chọn đầu tiên mặc định của bạn. [...] Tuy nhiên, QList được sử dụng trong các API Qt để chuyển các tham số và trả về giá trị. Sử dụng QList để giao tiếp với các API đó. " (Tài liệu QList)
AntonyG

63

Mọi thứ đã thay đổi

Bây giờ chúng ta đang ở Qt 5,8 và mọi thứ đã thay đổi, vì vậy tài liệu. Nó đưa ra một câu trả lời rõ ràng và khác biệt cho câu hỏi này:

QVectornên là lựa chọn đầu tiên mặc định của bạn. QVector<T>thường sẽ cho hiệu suất tốt hơn QList<T>, vì QVector<T>luôn lưu trữ các mục của nó tuần tự trong bộ nhớ, nơi QList<T>sẽ phân bổ các mục của nó trên heap trừ khi sizeof(T) <= sizeof(void*)và T đã được khai báo là a Q_MOVABLE_TYPEhoặc a đang Q_PRIMITIVE_TYPEsử dụng Q_DECLARE_TYPEINFO.

Xem Ưu điểm và Nhược điểm của việc sử dụng QListđể được giải thích. Tuy nhiên, QListđược sử dụng xuyên suốt các API Qt để truyền các tham số và trả về giá trị. Sử dụng QListđể giao tiếp với các API đó.


2
Điều này sẽ tăng lên và bây giờ được chấp nhận là câu trả lời.
ymoreau

12

Trong QVectortương tự như std::vector. QLinkedListtương tự như std::list. QListlà một vectơ dựa trên chỉ mục, nhưng vị trí bộ nhớ không được đảm bảo (như std::deque).


3

Từ tài liệu QtList:

  • QList được sử dụng trong hầu hết các trường hợp. Đối với cấu trúc có một nghìn mục, cho phép chèn hiệu quả vào giữa và cung cấp quyền truy cập được lập chỉ mục. prepend()append()rất nhanh vì bộ nhớ được phân bổ trước ở cả hai đầu của mảng bên trong. QList<T>là một mảng con trỏ kiểu T. Nếu T có kiểu con trỏ giống như con trỏ dùng chung hoặc Qt, đối tượng được lưu trực tiếp trong mảng

  • QVectorđược ưu tiên trong trường hợp có nhiều append()hoặc insert()nhiều mục mới có kích thước lớn hơn một con trỏ vì QVectorcấp phát bộ nhớ cho các mục của nó trong một phân bổ heap. Đối với việc QListchèn phần phụ của một mục mới yêu cầu cấp phát bộ nhớ của mục mới trên heap. Tóm lại, nếu bạn muốn các mục chiếm các vị trí bộ nhớ liền kề hoặc nếu các mục của bạn lớn hơn một con trỏ và bạn muốn tránh phí phân bổ chúng trên heap riêng lẻ tại thời điểm chèn, thì hãy sử dụng QVector.


-2

QVector giống như một mảng có thể thay đổi kích thước (tăng hoặc giảm) nhưng nó đi kèm với các giao dịch và tính toán và thời gian lớn.

Ví dụ, nếu bạn muốn thêm một mục, một mảng mới được tạo, tất cả các mục được sao chép sang mảng mới, mục mới được thêm vào cuối và mảng cũ bị xóa. Và ngược lại để xóa là tốt.

Tuy nhiên, QLinkedListhoạt động với con trỏ. Vì vậy, khi một mục mới được tạo, chỉ một không gian bộ nhớ mới được cấp phát và liên kết với phần bộ nhớ duy nhất. Vì nó hoạt động với con trỏ nên nhanh hơn và hiệu quả hơn.

Nếu bạn có danh sách các mục mà bạn không mong đợi thay đổi kích thước nhiều, QVectorcó lẽ là tốt, nhưng thường QLinkedListđược sử dụng cho hầu hết các mục đích.


3
-1: QVector không đi kèm với các giao dịch nặng như bạn yêu cầu bởi vì nó phân bổ trước và có chiến lược phát triển / thu nhỏ tốt để bổ sung và mở rộng. Thêm / chèn trước thực sự yêu cầu các phạm vi dữ liệu di chuyển, nhưng vì chúng liên tục trong bộ nhớ nên việc này được thực hiện rất nhanh chóng (bộ nhớ cache có thể hoạt động tốt với dữ liệu liên tục, không giống như với các khối đống phân tán như với QList). Tuyên bố thứ hai của bạn rằng QLinkedList được sử dụng cho hầu hết các mục đích là hoàn toàn sai. Nó hiếm khi được sử dụng nhất. Bạn có thể nhầm lẫn nó với QList?
DerManu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.