Tại sao ArrayDeque tốt hơn LinkedList


160

Tôi đang cố gắng để hiểu tại sao ArrayDeque của Java tốt hơn LinkedList của Java khi cả hai đều thực hiện giao diện Deque.

Tôi hầu như không thấy ai đó sử dụng ArrayDeque trong mã của họ. Nếu ai đó làm sáng tỏ hơn về cách thức thực hiện ArrayDeque, nó sẽ hữu ích.

Nếu tôi hiểu nó, tôi sẽ tự tin hơn khi sử dụng nó. Tôi không thể hiểu rõ việc triển khai JDK như cách nó quản lý các tham chiếu đầu và đuôi.


5
Nhìn vào câu trả lời trong câu hỏi này tôi đã thực hiện vài ngày trước: stackoverflow.com/questions/6129805/ trên
Renato Dinhani

1
Trong thư mục, nơi bạn đã cài đặt jdk, có một tệp src.zip. Nó là kho lưu trữ với mã nguồn của các lớp java. Tôi thực sự khuyên bạn nên nghiên cứu các cấu trúc lớp và các lớp bên trong này để hiểu rõ hơn về cách các lớp java hoạt động.

Câu trả lời:


155

Các cấu trúc được liên kết có thể là cấu trúc tồi tệ nhất để lặp lại với lỗi bộ nhớ cache trên mỗi thành phần. Trên hết, họ tiêu thụ nhiều bộ nhớ hơn.

Nếu bạn cần thêm / xóa cả hai đầu, ArrayDeque tốt hơn đáng kể so với danh sách được liên kết. Truy cập ngẫu nhiên mỗi phần tử cũng là O (1) cho hàng đợi tuần hoàn.

Hoạt động tốt hơn duy nhất của danh sách được liên kết là loại bỏ phần tử hiện tại trong quá trình lặp.


56
Một điểm khác biệt cần lưu ý: LinkedList hỗ trợ các phần tử null, trong khi ArrayDeque thì không.
Luke Usherwood

14
Ngoài ra một nhược điểm nhỏ khác (đối với các ứng dụng thời gian thực) là trên thao tác đẩy / thêm, sẽ mất thêm một chút khi mảng bên trong của ArrayDeque đầy, vì nó phải tăng gấp đôi kích thước và sao chép tất cả dữ liệu.
Andrei I

4
@AndreiI, đây chỉ là một mặt của câu chuyện. Ngay cả khi bạn loại trừ chi phí lặp cho ứng dụng thời gian thực và khả năng tăng cường khả năng cần thiết, thì GC có thể cần phải lặp lại toàn bộ LinkedList. Về cơ bản, bạn đang chuyển các chi phí (cao hơn để khởi động) vào GC.
bestsss

3
@DavidT. b / c nó liên quan đến chi phí GC của nút được giải phóng, việc gán đầu cũng có thể yêu cầu đánh dấu thẻ (đối với GC một lần nữa, nếu LinkedList đã có trong gen được thuê) ... và đó là trên đầu của phần bổ sung (bộ đệm- bỏ lỡ) để trả về phần tử và phát lại.
bestsss

1
Ngoài ra, LinkedListthực hiện Listtrong khi ArrayDequekhông. Điều này có nghĩa là LinkedListcó các phương thức như indexOfhoặc remove(int)trong khi ArrayDequekhông có. Nó có thể quan trọng đôi khi.
ZhekaKozlov

60

Tôi tin rằng nút thắt hiệu năng chính LinkedListlà thực tế là bất cứ khi nào bạn đẩy đến bất kỳ điểm cuối nào, phía sau hiện trường, việc triển khai sẽ phân bổ một nút danh sách liên kết mới, về cơ bản liên quan đến JVM / OS và điều đó rất tốn kém. Ngoài ra, bất cứ khi nào bạn bật từ bất kỳ đầu nào, các nút bên trong LinkedListtrở thành đủ điều kiện để thu gom rác và đó là công việc nhiều hơn đằng sau hiện trường. Ngoài ra, do các nút danh sách được liên kết được phân bổ ở đây và ở đó, việc sử dụng bộ đệm CPU sẽ không mang lại nhiều lợi ích.

Nếu nó có thể được quan tâm, tôi có một bằng chứng rằng việc thêm (nối thêm) một phần tử vào ArrayListhoặc ArrayDequechạy trong thời gian không đổi được khấu hao; đề cập đến điều này .


26

ArrayDeque là mới với Java 6, đó là lý do tại sao rất nhiều mã (đặc biệt là các dự án cố tương thích với các phiên bản Java trước đó) không sử dụng nó.

Nó "tốt hơn" trong một số trường hợp vì bạn không phân bổ một nút cho mỗi mục để chèn; thay vào đó, tất cả các phần tử được lưu trữ trong một mảng khổng lồ, được thay đổi kích thước nếu nó đầy.


17

Tất cả những người chỉ trích a LinkedList, nghĩ về mọi anh chàng khác đang sử dụng ListJava có thể sử dụng ArrayListLinkedListhầu hết thời gian bởi vì họ đã có trước Java 6 và bởi vì đó là những người được dạy như một khởi đầu trong hầu hết các cuốn sách.

Nhưng, điều đó không có nghĩa, tôi sẽ mù quáng đứng LinkedListvề ArrayDequephía họ. Nếu bạn muốn biết, hãy xem điểm chuẩn dưới đây được thực hiện bởi Brian .

Thiết lập thử nghiệm xem xét:

  • Mỗi đối tượng thử nghiệm là một chuỗi 500 ký tự. Mỗi chuỗi là một đối tượng khác nhau trong bộ nhớ.
  • Kích thước của mảng thử nghiệm sẽ được thay đổi trong các thử nghiệm.
  • Đối với mỗi kích thước mảng / kết hợp thực hiện Hàng đợi, 100 thử nghiệm được chạy và thời gian trung bình cho mỗi thử nghiệm được tính toán.
  • Mỗi bài kiểm tra bao gồm điền vào mỗi hàng đợi với tất cả các đối tượng, sau đó loại bỏ tất cả chúng.
  • Đo thời gian tính bằng mili giây.

Kết quả kiểm tra:

  • Dưới 10.000 phần tử, cả hai bài kiểm tra LinkedList và ArrayDeque đều tính trung bình ở mức 1 ms phụ.
  • Khi các bộ dữ liệu trở nên lớn hơn, sự khác biệt giữa thời gian kiểm tra trung bình của ArrayDeque và LinkedList sẽ lớn hơn.
  • Với kích thước thử nghiệm là 9,900,000 phần tử, cách tiếp cận LinkedList mất ~ 165% so với cách tiếp cận ArrayDeque.

Biểu đồ:

nhập mô tả hình ảnh ở đây

Lấy đi:

  • Nếu yêu cầu của bạn là lưu trữ 100 hoặc 200 phần tử, nó sẽ không tạo ra nhiều sự khác biệt khi sử dụng một trong hai Hàng đợi.
  • Tuy nhiên, nếu bạn đang phát triển trên thiết bị di động, bạn có thể muốn sử dụng ArrayListhoặc ArrayDequedự đoán khả năng tối đa mà danh sách có thể được yêu cầu là do hạn chế bộ nhớ nghiêm ngặt.
  • Rất nhiều mã tồn tại, được viết bằng cách sử dụng một LinkedListcách cẩn thận khi quyết định sử dụng một ArrayDequeđặc biệt bởi vì nó KHÔNG thực hiện Listgiao diện (tôi nghĩ đó là lý do đủ lớn). Có thể là cơ sở mã của bạn nói chuyện với giao diện Danh sách một cách rộng rãi, rất có thể và bạn quyết định nhảy vào với một ArrayDeque. Sử dụng nó để triển khai nội bộ có thể là một ý tưởng tốt ...

Làm thế nào để điểm chuẩn này nắm bắt được thời gian GC gây ra bởi rác danh sách được liên kết?
0xbe5077ed

3

ArrayDequeLinkedList đang triển khai giao diện Deque nhưng việc thực hiện thì khác.

Sự khác biệt chính:

  1. Lớp ArrayDeque là triển khai mảng có thể thay đổi kích thước của giao diện Deque và lớp LinkedList là triển khai danh sách

  2. Các phần tử NULL có thể được thêm vào LinkedList nhưng không có trong ArrayDeque

  3. ArrayDeque hiệu quả hơn so với LinkedList để thêm và xóa hoạt động ở cả hai đầu và việc thực hiện LinkedList là hiệu quả để loại bỏ phần tử hiện tại trong quá trình lặp

  4. Việc triển khai LinkedList tiêu tốn nhiều bộ nhớ hơn ArrayDeque

Vì vậy, nếu bạn không phải hỗ trợ các phần tử NULL && tìm kiếm ít bộ nhớ hơn && hiệu quả của việc thêm / xóa các phần tử ở cả hai đầu, ArrayDeque là tốt nhất

Tham khảo tài liệu để biết thêm chi tiết.


13
"Trong danh sách được liên kết, sẽ mất O (N) để tìm phần tử cuối cùng." không chính xác LinkedList được triển khai dưới dạng danh sách được liên kết đôi, do đó bạn không phải duyệt qua danh sách để lấy phần tử cuối cùng ( header.previous.element). Yêu cầu "hiệu quả bộ nhớ" cũng có thể bị thách thức do mảng sao lưu luôn được thay đổi kích thước thành sức mạnh tiếp theo là 2.
Clément MATHIEU

5
"Sẽ mất O (N) để tìm phần tử cuối cùng" là SAI. Danh sách được liên kết giữ một tham chiếu đến nút cuối cùng và LinkedList.desceinatingIterator () có được nút đó. Vì vậy, chúng tôi nhận được hiệu suất O (1). Xem: coffeeorientedprogramming.wordpress.com/2018/04/23/ Cách (do đó hạ cấp).
Leo Ufimtsev

1
Khi bạn sử dụng một Iteratorđể truy cập phần tử cuối cùng, thao tác là O (N) cho cả hai lớp. Khi bạn sử dụng Dequegiao diện chung , truy cập phần tử cuối cùng là O (1) cho cả hai lớp. Bất kể quan điểm nào bạn đưa ra, việc gán O (1) cho ArrayDequevà O (N) LinkedListcùng một lúc, là sai.
Holger

Tất cả các đề xuất của bạn được thực hiện và sửa chữa nội dung
Ravindra babu

1

Mặc dù ArrayDeque<E>LinkedList<E>đã thực hiện cả Deque<E>Giao diện, nhưng ArrayDeque về cơ bản sử dụng mảng Object E[]để giữ các phần tử bên trong Object, do đó, nó thường sử dụng chỉ mục để định vị các phần tử đầu và đuôi.

Nói một cách dễ hiểu, nó chỉ hoạt động như Deque (với tất cả phương thức của Deque), tuy nhiên sử dụng cấu trúc dữ liệu của mảng. Liên quan đến cái nào tốt hơn, phụ thuộc vào cách thức và nơi bạn sử dụng chúng.


-1

Không phải lúc nào cũng như vậy.

Ví dụ, trong trường hợp dưới đây linkedlistcó hiệu suất tốt hơn so ArrayDequevới mã leet 103.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> rs=new ArrayList<>();
        if(root==null)
            return rs;
        // 👇 here ,linkedlist works better
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        boolean left2right=true;
        while(!queue.isEmpty())
        {
            int size=queue.size();
            LinkedList<Integer> t=new LinkedList<>();
            while(size-->0)
            {
                TreeNode tree=queue.remove();
                if(left2right)  
                    t.add(tree.val);
                else
                    t.addFirst(tree.val);
                if(tree.left!=null)
                {
                    queue.add(tree.left);
                }
                if(tree.right!=null)
                {
                    queue.add(tree.right);
                }
            }
            rs.add(t);
            left2right=!left2right;
        }
        return rs;
    }
}

-5

Độ phức tạp thời gian cho ArrayDeque để truy cập một phần tử là O (1) và đối với LinkList là O (N) để truy cập phần tử cuối cùng. ArrayDeque không phải là luồng an toàn nên việc đồng bộ hóa thủ công là cần thiết để bạn có thể truy cập nó thông qua nhiều luồng và vì vậy chúng nhanh hơn.


3
Nếu bạn đang đề cập đến LinkedListtrong Java Collection, nó được liên kết đôi và có quyền truy cập nhanh vào phần đầu và phần đuôi, vì vậy việc truy cập vào phần tử cuối cùng cũng mất O (1).
Maurice

Truy cập phần tử cuối cùng trong LinkedList không phải là O (N). Nếu bạn sử dụng desceinatingIterator (), nó được thực hiện trong O (1). Xem coffeeorientedprogramming.wordpress.com/2018/04/23/ Cách (do đó downvote).
Leo Ufimtsev

1
Cả hai lớp không phải là chủ đề an toàn. Và không có mối liên hệ nào giữa phần đầu của câu Đồng bộ hóa thủ công là cần thiết và phần cuối của nó là nhanh hơn.
Holger
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.