ArrayList vs. LinkedList vs. Vector

1. List Overview

List, as its name indicates, is an ordered sequence of elements. When we talk about List, it is a good idea to compare it with Set which is a set of unique and unordered elements. The following is the class hierarchy diagram of Collection. From the hierarchy diagram you can get a general idea of Java Collections.

2. ArrayList vs. LinkedList vs. Vector

From the hierarchy diagram, they all implement List interface. They are very similar to use. Their main difference is their implementation which causes different performance for different operations.

ArrayList is implemented as a resizable array. As more elements are added to ArrayList, its size is increased dynamically. It’s elements can be accessed directly by using the get and set methods, since ArrayList is essentially an array.

LinkedList is implemented as a double linked list. Its performance on add and remove is better than Arraylist, but worse on get and set methods.

Vector is similar with ArrayList, but it is synchronized.

ArrayList is a better choice if your program is thread-safe. Vector and ArrayList require more space as more elements are added. Vector each time doubles its array size, while ArrayList grow 50% of its size each time. LinkedList, however, also implements Queue interface which adds more methods than ArrayList and Vector, such as offer(), peek(), poll(), etc.

Note: The default initial capacity of an ArrayList is pretty small. It is a good habit to construct the ArrayList with a higher initial capacity. This can avoid the resizing cost.

3. ArrayList example

ArrayList<Integer> al = new ArrayList<Integer>();
al.add(3);
al.add(2);		
al.add(1);
al.add(4);
al.add(5);
al.add(6);
al.add(6);
 
Iterator<Integer> iter1 = al.iterator();
while(iter1.hasNext()){
	System.out.println(iter1.next());
}

4. LinkedList example

LinkedList<Integer> ll = new LinkedList<Integer>();
ll.add(3);
ll.add(2);		
ll.add(1);
ll.add(4);
ll.add(5);
ll.add(6);
ll.add(6);
 
Iterator<Integer> iter2 = ll.iterator();
while(iter2.hasNext()){
	System.out.println(iter2.next());
}

As shown in the examples above, they are similar to use. The real difference is their underlying implementation and their operation complexity.

5. Vector

Vector is almost identical to ArrayList, and the difference is that Vector is synchronized. Because of this, it has an overhead than ArrayList. Normally, most Java programmers use ArrayList instead of Vector because they can synchronize explicitly by themselves.

6. Performance of ArrayList vs. LinkedList

The time complexity comparison is as follows:
arraylist-vs-linkedlist-complexity

* add() in the table refers to add(E e), and remove() refers to remove(int index)

  • ArrayList has O(n) time complexity for arbitrary indices of add/remove, but O(1) for the operation at the end of the list.
  • LinkedList has O(n) time complexity for arbitrary indices of add/remove, but O(1) for operations at end/beginning of the List.
  •  

I use the following code to test their performance:

ArrayList<Integer> arrayList = new ArrayList<Integer>();
LinkedList<Integer> linkedList = new LinkedList<Integer>();
 
// ArrayList add
long startTime = System.nanoTime();
 
for (int i = 0; i < 100000; i++) {
	arrayList.add(i);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("ArrayList add:  " + duration);
 
// LinkedList add
startTime = System.nanoTime();
 
for (int i = 0; i < 100000; i++) {
	linkedList.add(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("LinkedList add: " + duration);
 
// ArrayList get
startTime = System.nanoTime();
 
for (int i = 0; i < 10000; i++) {
	arrayList.get(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("ArrayList get:  " + duration);
 
// LinkedList get
startTime = System.nanoTime();
 
for (int i = 0; i < 10000; i++) {
	linkedList.get(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("LinkedList get: " + duration);
 
 
 
// ArrayList remove
startTime = System.nanoTime();
 
for (int i = 9999; i >=0; i--) {
	arrayList.remove(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("ArrayList remove:  " + duration);
 
 
 
// LinkedList remove
startTime = System.nanoTime();
 
for (int i = 9999; i >=0; i--) {
	linkedList.remove(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("LinkedList remove: " + duration);

And the output is:

ArrayList add:  13265642
LinkedList add: 9550057
ArrayList get:  1543352
LinkedList get: 85085551
ArrayList remove:  199961301
LinkedList remove: 85768810

arraylist-vs-linkedlist

The difference of their performance is obvious. LinkedList is faster in add and remove, but slower in get. Based on the complexity table and testing results, we can figure out when to use ArrayList or LinkedList. In brief, LinkedList should be preferred if:

  • there are no large number of random access of element
  • there are a large number of add/remove operations

22 thoughts on “ArrayList vs. LinkedList vs. Vector”

  1. I believe the add of an ArrayList has amortized complexity of O(1) because it has to double the array every time the buffer is full and we do an add; however, the LinkedList is just O(1) assuming the linkedlist keeps track of the tail of the list.

  2. I just run the given program and found unexpected result in case of add element in arraylist and linkedlist. Add in linkedlist taking much time then array list. I was amazed looking the results. So I run only add section removing other (get, remove), getting expected result. (i.e. – linkedlist is faster in adding the element then arraylist).

    ArrayList add: 7879257
    LinkedList add: 8587097
    ArrayList get: 93867
    LinkedList get: 91681319
    ArrayList remove: 471618335
    LinkedList remove: 90256678

    ArrayList add: 7274670
    LinkedList add: 8485124
    ArrayList get: 92160
    LinkedList get: 96073854
    ArrayList remove: 125224587
    LinkedList remove: 85604729

    ArrayList add: 8384430
    LinkedList add: 6260483
    ArrayList get: 151893
    LinkedList get: 92966440
    ArrayList remove: 135471844
    LinkedList remove: 87632677

    ArrayList add: 6741336
    LinkedList add: 10437551
    ArrayList get: 161706
    LinkedList get: 90528038
    ArrayList remove: 458203076
    LinkedList remove: 92194172

    ArrayList add: 6850563
    LinkedList add: 8046084
    ArrayList get: 168533
    LinkedList get: 90907772
    ArrayList remove: 467550493
    LinkedList remove: 94564734
    =================================
    ArrayList add: 7445337
    LinkedList add: 5977603

    ArrayList add: 5499309
    LinkedList add: 4195415

    ArrayList add: 7945816
    LinkedList add: 4072108

    ArrayList add: 7350616
    LinkedList add: 4373762

    ArrayList add: 10162351
    LinkedList add: 6481923

  3. “ArrayList” is a better choice if your program is thread-safe – i believe here it should be Vector

  4. “The difference of their performance is obvious. LinkedList is faster in add and remove, but slower in get. Based on the complexity table and testing results, we can figure out when to use ArrayList or LinkedList. In brief, LinkedList should be preferred if:..”

    It isn’t obvious. You didn’t mentioned which Java version did you use for the benchmark. It was true in Java 1.5 and under.

    You made a fatal mistake here. Put your whole benchmark code in a procedure and call it 2-4 times. You will get completely different numbers:

    First call with max value 13 (it’s higher than the default capacity of the ArrayList):

    ArrayList add: 324103 (OMG)
    LinkedList add: 8608

    ArrayList get: 3311 (2x faster)
    LinkedList get: 6621

    ArrayList get iterate: 101634 (slightly faster)
    LinkedList get iterate: 128119

    ArrayList remove: 4966 (moderately faster)
    LinkedList remove: 7283

    ———————————————

    4th call with max 13:

    ArrayList add: 3973 (OMG2 it’s like a magic)
    LinkedList add: 5297

    ArrayList get: 993 (2x faster)
    LinkedList get: 2318

    ArrayList get iterate: 3642 (almost the same)
    LinkedList get iterate: 3641

    ArrayList remove: 1656 (2x faster)
    LinkedList remove: 3311

    ——————————————

    4th call with max 10000

    ArrayList add: 131098 (moderately faster)
    LinkedList add: 189032

    ArrayList get: 305233 (15x faster)
    LinkedList get: 43127889

    ArrayList get iterate: 37409 (almost 2x faster)
    LinkedList get iterate: 68859

    ArrayList remove: 45355 (almost 2x faster)
    LinkedList remove: 85743

    The magic what happens is called bytecode optimalization. Java is fast today.

    —————————————————————————————————————————–
    Conclusion:
    1) Always use ArrayList except you need to insert always at the front
    2) Benchmark your application if it is slow
    3) Don’t use premature optimalization because you don’t know what happens under the hood in the JVM

  5. It is worth noting that in this example as its known the list size is 10,0000 then the ArrayList is actually faster for insertions if initialized with the desired size, thereby creating just one array to back the list. Like so:
    ArrayList arrayList = new ArrayList(10000);

  6. Just boning up on Java, coming back to it after a long hiatus when working in C/C++. Adding to an array one element at a time is of course going to be slow, as the system must find storage for the size of the existing array +1. This isn’t a very smart approach to using extensible arrays, or anything for that matter.

    Why not allocate arrays arbitrarily large with a constant or statistically managed chunk size so many elements can be appended onto the array before any additional memory needs to be allocated? If you’re on a machine with enough resources to run Java (instead of C/C++) the you can spare the temporarily unused memory, so why not use this approach?

    TVMIA

  7. Under point 6 in table there is a mistake for add() method – it should be reversed, as it is amortized for ArrayList (it does reallocation not each method call) and it is constant for LinkedList

  8. Thanks a lot for this article, it was very helpfull.
    There is a little mistake in ‘4. LinkedList example’ at line with iter2 implementation. Convert ‘al’ to ‘ll’;)

  9. As mentioned, unfortunately both ArrayList and LinkedList have their drawbacks: ArrayList is slow if
    insertion/removal does not happen at the end and LinkedList is slow at
    accessing elements by index.

    There is a new list implementation called GapList which combines the strengths of both ArrayList
    and LinkedList. It has been designed as drop-in replacement for both ArrayList and LinkedList and therefore implements both the interfaces List and Deque.

    GapList’s implementation guarantees efficient random access to elements by index (as ArrayList does) and at the same time efficient adding and removing elements to and from head and tail of the list (as LinkedList does).

    You find more information about GapList at http://java.dzone.com/articles/gaplist-%E2%80%93-lightning-fast-list.

Leave a Comment