Tôi khuyên bạn nên chống lại std::forward_list
giống như tôi khuyên bạn nên chống lại std::list
trong hầu hết mọi tình huống. Cá nhân, tôi chưa bao giờ tìm thấy một tình huống trong mã của tôi, nơi một danh sách liên kết là cấu trúc dữ liệu tốt nhất.
Trong C++, bộ sưu tập dữ liệu truy cập mặc định của bạn phải là std::vector
. Nó cung cấp cho bạn hiệu quả push_back
, nếu đó là những gì bạn thực sự cần. Về mặt kỹ thuật, nó không cung cấp cho bạn hiệu quả xóa và chèn từ giữa nếu bạn chỉ nhìn vào các phép đo phức tạp lớn-O trừu tượng của một phép toán đó.Tuy nhiên, trong thế giới thực, std::vector
vẫn thắng ngay cả khi chèn và xóa ở giữa.
Ví dụ: Bjarne Stroustrup đã tạo thử nghiệm 100.000 phần tử std::list
và std::vector
. Anh ta sẽ tìm kiếm từng phần tử và xóa nó. Sau đó, anh ta sẽ tìm thấy một điểm chèn và chèn vào giữa. Anh ta có thể sử dụng tìm kiếm nhị phân trên std::vector
, nhưng không làm cho việc so sánh 'công bằng hơn'.
Kết quả cho thấy một chiến thắng mạnh mẽ cho std::vector
, ngay cả trong trường hợp này, nơi std::list
được cho là mạnh mẽ. Đơn giản chỉ cần đi qua các std::list
mất nhiều thời gian hơn vì cách xa nhau trong bộ nhớ tất cả các đối tượng được. std::list
không thân thiện với bộ nhớ cache, có thể là điều quan trọng nhất đối với các bộ vi xử lý hiện đại.
The complete talk by Bjarne Stroustrup
Thorough explanation of the effects, with benchmarks at multiple sizes
Lưu ý rằng liên kết thứ hai này ở đây đưa ra một số tình huống mà bạn có thể có thể muốn sử dụng một std::list
, chẳng hạn như khi kích thước của các yếu tố là lớn. Tuy nhiên, tôi đã ở trong một tình huống mà tôi có nhiều yếu tố theo một thứ tự cụ thể và cần phải xóa một số.
Các thành phần này lớn hơn bất kỳ loại được cài sẵn nào, nhưng không lớn, có lẽ 20-30 byte mỗi trên máy tính 32 bit). Số lượng các phần tử đủ lớn để toàn bộ cấu trúc dữ liệu của tôi là vài trăm MiB. Thu thập dữ liệu là tập hợp các giá trị về mặt lý thuyết có thể là hợp lệ dựa trên thông tin hiện đã biết. Thuật toán được lặp lại trên tất cả các phần tử và các phần tử đã xóa không còn hợp lệ dựa trên thông tin mới nữa, với mỗi lần vượt qua có thể xóa khoảng 80% các phần tử còn lại.
Triển khai đầu tiên của tôi là cách tiếp cận đơn giản std::vector
nơi tôi đã xóa các phần tử không hợp lệ khi tôi duyệt qua. Điều này làm việc cho các bộ dữ liệu thử nghiệm nhỏ, nhưng khi tôi cố gắng làm điều thực sự, nó quá chậm để có ích. Tôi đã chuyển sang std::list
làm vùng chứa, nhưng sử dụng cùng một thuật toán và tôi thấy các cải thiện hiệu suất đáng kể. Tuy nhiên, nó vẫn còn quá chậm để có ích. Thay đổi chiến thắng là chuyển đổi về số std::vector
, nhưng thay vì xóa các yếu tố ở vị trí xấu, tôi đã tạo một std::vector
mới và bất kỳ yếu tố nào tôi thấy tốt đều được đưa vào đó std::vector
và sau đó ở cuối hàm Tôi chỉ đơn giản là loại bỏ các std::vector
cũ và sử dụng mới, và điều này đã cho tôi về nhiều tốc độ lên trên std::list
như std::list
đã cho tôi hơn thực hiện std::vector
ban đầu của tôi, và điều này chỉ đủ nhanh để có ích.
Lưu ý rằng danh sách 'std :: hai chiều 'cũng hỗ trợ" chèn nhanh và xóa các phần tử từ bất kỳ đâu khỏi vùng chứa "và có' push_back'. Chi phí là một con trỏ thêm cho mỗi mục. Bộ nhớ có quá chặt đến nỗi bạn không thể sử dụng nó? –
Tại sao bạn cần nó? Bạn có muốn phát triển danh sách của mình theo cả hai cách không? Bạn không thể sử dụng 'push_front()' một cách dễ dàng? –
Tôi muốn tiếp tục phân loại danh sách –