252

I am trying to to understand why Java's ArrayDeque is better than Java's LinkedList as they both implement Deque interface.

I hardly see someone using ArrayDeque in their code. If someone sheds more light into how ArrayDeque is implemented, it would be helpful.

If I understand it, I will be more confident using it. I could not clearly understand the JDK implementation as to the way it manages head and tail references.

Jeremy S.
  • 6,423
  • 13
  • 48
  • 67
Cruel
  • 2,523
  • 2
  • 15
  • 4
  • 5
    Look at the answer in this question I done days ago: http://stackoverflow.com/questions/6129805/what-is-the-fastest-java-collection-with-the-basic-functionality-of-a-queue – Renato Dinhani May 28 '11 at 17:19
  • 2
    In the folder, where you have your jdk installed, there is a file `src.zip`. It is the archive with the source code of java classes. I strongly recommend to study these classes structure and internals to get better understanding how do java classes work. –  Sep 17 '15 at 07:50
  • One more point. The default size of ArrayDeque is 16. ArrayDeque doubles its size when it is full. Elements are copied to the new array after the size doubles. It is better to initialize ArrayDeque with an initial size. – Vineeth Chitteti Aug 27 '20 at 01:36
  • Another point worth mentioning is that on LinkedList you can use indexes to iterate through its elements while ArrayDeque does not support index based access. – Java Main Apr 16 '21 at 23:05

10 Answers10

240

Linked structures are possibly the worst structure to iterate with a cache miss on each element. On top of it they consume way more memory.

If you need add/remove of the both ends, ArrayDeque is significantly better than a linked list. Random access each element is also O(1) for a cyclic queue.

The only better operation of a linked list is removing the current element during iteration.

bestsss
  • 11,796
  • 3
  • 53
  • 63
  • 110
    Another difference to bear in mind: LinkedList supports null elements, whereas ArrayDeque does not. – Luke Usherwood Jul 10 '13 at 17:24
  • 30
    Also another small disadvantage (for real-time applications) is that on a push/add operation it takes a bit more when the internal array of the ArrayDeque is full, as it has to double its size and copy all the data. – V G Sep 13 '13 at 15:35
  • 8
    @AndreiI, this only one side of the story. Even if you exclude the iteration costs for real time application and ability to prealloc the needed capacity, the GC may need to iterate the entire LinkedList. Basically you are moving the costs (which are higher to boot) into the GC. – bestsss May 19 '14 at 05:26
  • @bestsss why would adding/removing on the head be worse off for the LinkedList? – David T. Oct 16 '14 at 19:05
  • 4
    @DavidT. b/c it involves GC costs of the freed node, assigning the head may also require card marking (for the GC again, if the LinkedList is already in the tenured gen)... and that's on top of the extra indirection (cache-miss) to return the element and relink. – bestsss Oct 17 '14 at 01:37
  • 7
    Also, `LinkedList` implements `List` while `ArrayDeque` doesn't. This means that `LinkedList` have methods like `indexOf` or `remove(int)` while `ArrayDeque` hasn't. It can be important sometimes. – ZhekaKozlov Sep 02 '15 at 11:16
  • kind late to this party. For `The only better operation of a linked list is removing the current element during iteration.` do u mean the method like `remove()` which remove the head of the queue? because linkedlist is also fail-fast. – Huazhe Yin Aug 06 '19 at 03:55
  • 1
    @HuazheYin calling `remove()` on the `Iterator` or `ListIterator`… – Holger Apr 08 '20 at 12:16
  • Linked structures are possibly the worst structure to iterate with a cache miss on each element. -------what does it mean? What is a cache miss? – ZhaoGang May 21 '20 at 00:48
  • 1
    @ZhaoGang `ArrayDeque` resemble arrays and allocate memory in contiguous memory blocks, in contrast to `LinkedList` where the memory allocation are in non-contiguous memory space. Since the data is stored in contiguous space, data will be stored in cache pretty easily. That would be a time consuming task for `LinkedList` and hence chance for cache miss. – Rahul Raj Jun 17 '21 at 15:45
  • @bestsss isn't GC always linear time for both arrays and linked lists? It always needs to go through all the elements to know which ones were removed. Of course, due to cache locality and less indirection, iterating over the array would be faster, but both operations are O(N). – andresp Apr 18 '22 at 15:05
  • 1
    "If you need add/remove of the both ends, ArrayDeque is significantly better than a linked list." - Time complexity wise, adding/removing from both ends is O(1) in either structures (amortized), but ArrayDeque could be more efficient production-wise as others have pointed out due to GC involvement when LinkedList has to create/release its internal nodes when adding/removing at both ends. – Ryhan Jun 29 '22 at 18:57
111

I believe that the main performance bottleneck in LinkedList is the fact that whenever you push to any end of the deque, behind the scene the implementation allocates a new linked list node, which essentially involves JVM/OS, and that's expensive. Also, whenever you pop from any end, the internal nodes of LinkedList become eligible for garbage collection and that's more work behind the scene. Also, since the linked list nodes are allocated here and there, usage of CPU cache won't provide much benefit.

If it might be of interest, I have a proof that adding (appending) an element to ArrayList or ArrayDeque runs in amortized constant time; refer to this.

coderodde
  • 1,269
  • 4
  • 17
  • 34
  • 4
    Can you tell me how adding/removing element from head is better in Linked that in Array? Shouldn't LinkedList have advantage as only references to `prev` and `next` are changed, while in ArrayDeque many elements need to be shifted? – Stefan Nov 15 '20 at 17:25
44

All the people criticizing a LinkedList, think about every other guy that has been using List in Java probably uses ArrayList and an LinkedList most of the times because they have been before Java 6 and because those are the ones being taught as a start in most books.

But, that doesn't mean, I would blindly take LinkedList's or ArrayDeque's side. If you want to know, take a look at the below benchmark done by Brian (archived).

The test setup considers:

  • Each test object is a 500 character String. Each String is a different object in memory.
  • The size of the test array will be varied during the tests.
  • For each array size/Queue-implementation combination, 100 tests are run and average time-per-test is calculated.
  • Each tests consists of filling each queue with all objects, then removing them all.
  • Measure time in terms of milliseconds.

Test Result:

  • Below 10,000 elements, both LinkedList and ArrayDeque tests averaged at a sub 1 ms level.
  • As the sets of data get larger, the differences between the ArrayDeque and LinkedList average test time gets larger.
  • At the test size of 9,900,000 elements, the LinkedList approach took ~165% longer than the ArrayDeque approach.

Graph:

enter image description here

Takeaway:

  • If your requirement is storing 100 or 200 elements, it wouldn't make much of a difference using either of the Queues.
  • However, if you are developing on mobile, you may want to use an ArrayList or ArrayDeque with a good guess of maximum capacity that the list may be required to be because of strict memory constraint.
  • A lot of code exists, written using a LinkedList so tread carefully when deciding to use a ArrayDeque especially because it DOESN'T implement the List interface(I think that's reason big enough). It may be that your codebase talks to the List interface extensively, most probably and you decide to jump in with an ArrayDeque. Using it for internal implementations might be a good idea...
drac_o
  • 427
  • 5
  • 11
Manish Kumar Sharma
  • 12,982
  • 9
  • 58
  • 105
32

ArrayDeque and LinkedList are implementing Deque interface but implementation is different.

Refer to Oracle documentation for more details.

Key differences:

  1. The ArrayDeque class is the resizable array implementation of the Deque interface and LinkedList class is the list implementation
  2. NULL elements can be added to LinkedList but not in ArrayDeque
  3. ArrayDeque is more efficient than the LinkedList for add and remove operation at both ends and LinkedList implementation is efficient for removing the current element during the iteration
  4. The LinkedList implementation consumes more memory than the ArrayDeque

So if you don't have to support NULL elements && looking for less memory && efficiency of add/remove elements at both ends, ArrayDeque is the best

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
  • 19
    "In Linked list, it will take O(N) to find last element." is not exactly true. LinkedList is implemented as a doubly linked list so you don't have to traverse the list to get the last element (`header.previous.element`). The "memory efficiency" claim can also be challenged since the backing array is always resized to the next power of 2. – Clément MATHIEU Sep 21 '15 at 16:06
  • 7
    "It will take O(N) to find last element" is WRONG. Linked list keeps a reference to the last node and LinkedList.descendingIterator() get's that node. So we get O(1) performance. See: https://coffeeorientedprogramming.wordpress.com/2018/04/23/linkedlist-descendingiterator-run-time/ (thus downvoting). – Leo Ufimtsev Apr 23 '18 at 19:11
  • 1
    When you use an `Iterator` to access the last element, the operation is O(N) for both classes. When you use the common `Deque` interface, accessing the last element is O(1) for both classes. Regardless of which point of view you take, attributing O(1) to `ArrayDeque` and O(N) to `LinkedList` at the same time, is wrong. – Holger Apr 08 '20 at 12:35
  • 1
    All your suggestions are taken and corrected the content – Ravindra babu Apr 25 '20 at 11:31
32

ArrayDeque is new with Java 6, which is why a lot of code (especially projects that try to be compatible with earlier Java versions) don't use it.

It's "better" in some cases because you're not allocating a node for each item to insert; instead all elements are stored in a giant array, which is resized if it gets full.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
3

I don't think ArrayDeque is better than LinkedList. They are different.

ArrayDeque is faster than LinkedList on average. But for adding an element, ArrayDeque takes amortized constant time, and LinkedList takes constant time.

For time-sensitive applications that require all operations to take constant time, only LinkedList should be used.

ArrayDeque's implementation uses arrays and requires resizing, and occasionally, when the array is full and needs to add an element, it will take linear time to resize, resulting the add() method taking linear time. That could be a disaster if the application is very time-sensitive.

A more detailed explanation of Java's implementation of the two data structures is available in the "Algorithms, Part I" course on Coursera offered by Princeton University, taught by Wayne and Sedgewick. The course is free to the public.

The details are explained in the video "Resizing Arrays" in the "Stacks and Queues" section of "Week 2".

anjiez
  • 31
  • 1
1

although ArrayDeque<E> and LinkedList<E> have both implemented Deque<E> Interface, but the ArrayDeque uses basically Object array E[] for keeping the elements inside its Object, so it generally uses index for locating the head and tail elements.

In a word, it just works like Deque (with all Deque's method), however uses array's data structure. As regards which one is better, depends on how and where you use them.

rekinyz
  • 6,576
  • 2
  • 29
  • 32
0

I'm not sure how ArrayDeque is implemented, but I have implemented something similar in C++ years ago, and I'm quite sure they do the same. the collection is actually an ArrayList which is considered "cyclic", i.e. the next item after a[n-1] is a[0]. the deque also keeps two pointers (integer) to the first and last item in the deque. When you addFirst you decread "first" (cyclic) and when addLast increase "last". It's quite simple and efficient.

ModdyFire
  • 706
  • 3
  • 9
  • 19
-1

That's not always the case.

For example, in the case below linkedlist has better performance than ArrayDeque according to leetcode 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;
    }
}
zappee
  • 20,148
  • 14
  • 73
  • 129
Red
  • 19
  • 8
-6

Time complexity for ArrayDeque for accessing a element is O(1) and that for LinkList is is O(N) to access last element. ArrayDeque is not thread safe so manually synchronization is necessary so that you can access it through multiple threads and so they they are faster.

Piyush Yawalkar
  • 252
  • 1
  • 4
  • 17
  • 6
    If you are referring to `LinkedList` in Java's `Collection`, it is doubly linked and has fast access to the head and the tail, so access to the last element also takes O(1). – Maurice Jul 15 '16 at 19:34
  • 1
    Accessing last element in LinkedList is not O(N). If you use descendingIterator(), it is performed in O(1). See https://coffeeorientedprogramming.wordpress.com/2018/04/23/linkedlist-descendingiterator-run-time/ (thus downvote). – Leo Ufimtsev Apr 23 '18 at 19:12
  • 1
    Both classes are not thread-safe. And there is no connection between the beginning of the sentence “manually synchronization is necessary” and its end “they are faster”. – Holger Apr 08 '20 at 12:41