TL; DR do kiến trúc máy tính hiện đại, ArrayList
sẽ hiệu quả hơn đáng kể đối với gần như mọi trường hợp sử dụng có thể - và do đó LinkedList
nên tránh ngoại trừ một số trường hợp rất độc đáo và cực đoan.
Về lý thuyết, LinkedList có O (1) cho add(E element)
Ngoài ra, việc thêm một yếu tố vào giữa danh sách sẽ rất hiệu quả.
Thực tiễn là rất khác nhau, vì LinkedList là một cấu trúc dữ liệu lưu trữ bộ nhớ cache . Từ hiệu suất POV - có rất ít trường hợp LinkedList
có thể hoạt động tốt hơn so với thân thiện với Cache ArrayList
.
Dưới đây là kết quả của một yếu tố chèn kiểm tra điểm chuẩn ở các vị trí ngẫu nhiên. Như bạn có thể thấy - danh sách mảng nếu hiệu quả hơn nhiều, mặc dù về lý thuyết, mỗi lần chèn ở giữa danh sách sẽ yêu cầu "di chuyển" n phần tử sau của mảng (giá trị thấp hơn sẽ tốt hơn):
Làm việc trên một phần cứng thế hệ sau (bộ nhớ cache lớn hơn, hiệu quả hơn) - kết quả thậm chí còn kết luận hơn:
LinkedList mất nhiều thời gian hơn để hoàn thành công việc tương tự. mã nguồn
Có hai lý do chính cho việc này:
Chủ yếu - rằng các nút của LinkedList
được phân tán ngẫu nhiên trên bộ nhớ. RAM ("Bộ nhớ truy cập ngẫu nhiên") không thực sự ngẫu nhiên và các khối bộ nhớ cần được tìm nạp vào bộ đệm. Thao tác này cần có thời gian và khi việc tìm nạp như vậy xảy ra thường xuyên - các trang bộ nhớ trong bộ đệm cần phải được thay thế mọi lúc -> Lỗi bộ nhớ cache -> Bộ nhớ cache không hiệu quả.
ArrayList
các phần tử được lưu trữ trên bộ nhớ liên tục - đó chính xác là những gì mà kiến trúc CPU hiện đại đang tối ưu hóa.
LinkedList
Yêu cầu thứ cấp để giữ lại con trỏ lùi / tiến, nghĩa là gấp 3 lần mức tiêu thụ bộ nhớ cho mỗi giá trị được lưu trữ so với ArrayList
.
DynamicIntArray , btw, là một triển khai ArrayList tùy chỉnh Int
(kiểu nguyên thủy) và không phải là Đối tượng - do đó tất cả dữ liệu thực sự được lưu trữ một cách ngẫu nhiên - do đó thậm chí còn hiệu quả hơn.
Một yếu tố quan trọng cần nhớ là chi phí tìm nạp khối bộ nhớ, có ý nghĩa lớn hơn chi phí truy cập vào một ô nhớ. Đó là lý do tại sao đầu đọc 1MB bộ nhớ tuần tự nhanh hơn tới x400 lần so với việc đọc lượng dữ liệu này từ các khối bộ nhớ khác nhau:
Latency Comparison Numbers (~2012)
----------------------------------
L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns 14x L1 cache
Mutex lock/unlock 25 ns
Main memory reference 100 ns 20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy 3,000 ns 3 us
Send 1K bytes over 1 Gbps network 10,000 ns 10 us
Read 4K randomly from SSD* 150,000 ns 150 us ~1GB/sec SSD
Read 1 MB sequentially from memory 250,000 ns 250 us
Round trip within same datacenter 500,000 ns 500 us
Read 1 MB sequentially from SSD* 1,000,000 ns 1,000 us 1 ms ~1GB/sec SSD, 4X memory
Disk seek 10,000,000 ns 10,000 us 10 ms 20x datacenter roundtrip
Read 1 MB sequentially from disk 20,000,000 ns 20,000 us 20 ms 80x memory, 20X SSD
Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms
Nguồn: Số trễ Mỗi lập trình viên nên biết
Để làm cho điểm rõ ràng hơn, vui lòng kiểm tra điểm chuẩn của việc thêm các yếu tố vào đầu danh sách. Đây là một trường hợp sử dụng trong đó, về mặt lý thuyết, LinkedList
nên thực sự tỏa sáng và ArrayList
sẽ đưa ra kết quả trường hợp kém hoặc thậm chí tệ hơn:
Lưu ý: đây là điểm chuẩn của lib C ++ Std, nhưng trải nghiệm trước đây của tôi cho thấy kết quả C ++ và Java rất giống nhau. Mã nguồn
Sao chép một lượng lớn bộ nhớ tuần tự là một hoạt động được tối ưu hóa bởi các CPU hiện đại - thay đổi lý thuyết và thực sự, một lần nữa, ArrayList
/ Vector
hiệu quả hơn nhiều
Tín dụng: Tất cả các điểm chuẩn được đăng ở đây được tạo bởi Kjell Hedström . Thậm chí nhiều dữ liệu có thể được tìm thấy trên blog của anh ấy