In Java, ArrayList stores elements in a resizable array, while LinkedList stores each element in a separate node linked to the previous and next nodes. The array gives ArrayList constant-time access by index and compact memory, so it is the right default in most LinkedList vs ArrayList decisions. LinkedList is faster only for adding or removing at the front or through an iterator, and for a queue or a stack, ArrayDeque fits better than either of them.
The following example runs the same operations on both classes and notes the cost of each line.
List<String> jobs = new ArrayList<>(List.of("report", "invoice", "photo"));
LinkedList<String> queue = new LinkedList<>(jobs);
String third = jobs.get(2); // "photo", reads one array slot
String thirdToo = queue.get(2); // "photo", walks the nodes
queue.addFirst("urgent"); // links one node, no shifting
jobs.add(0, "urgent"); // shifts 3 elements to the right
String next = queue.pollFirst(); // "urgent", LinkedList is also a Deque
ArrayList<String> converted = new ArrayList<>(queue); // [report, invoice, photo]
Both lists give the same results, only at a different cost. We look at how each class stores elements, compare the cost of every common operation, cover the cases where LinkedList still fits, and convert between the two.
1. How ArrayList and LinkedList Store Elements
An ArrayList keeps references to its elements in one Object[] array, next to each other in memory. A LinkedList is a doubly linked list, so it wraps every element in a Node object that holds the element and references to the previous and next nodes, and the list itself only remembers the first and last node.

The node class inside the JDK is a small static class with three fields.
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
Each node is a separate object with its own header. On a 64-bit HotSpot JVM with compressed references, which is the default for heaps under 32 GB, a node typically takes 24 bytes, whereas one array slot takes 4 bytes. A list of one million elements therefore spends about 24 MB on nodes in a LinkedList, compared with about 4 to 6 MB for the backing array of an ArrayList, before counting the elements themselves.
Both classes implement List, keep insertion order, allow duplicates and null, and are not synchronized. LinkedList also implements Deque, so it has queue methods such as offerFirst(), pollLast() and peekFirst().
2. Time Complexity Compared
The cost of each operation follows from the storage. The array makes index access cheap and inserting in the middle expensive, and the nodes make the ends cheap and everything that needs an index expensive.
| Operation | ArrayList | LinkedList |
|---|---|---|
| get(i), set(i, e) | O(1) | O(n), walks from the closer end |
| add(e) at the end | O(1) amortized | O(1) |
| addFirst(e), add(0, e) | O(n), shifts all elements | O(1) |
| add(i, e) in the middle | O(n), shifts the tail | O(n), walks to i and links in O(1) |
| removeFirst() | O(n), shifts all elements | O(1) |
| removeLast() | O(1) | O(1) |
| remove(i), remove(Object) | O(n) | O(n), the search dominates |
| Iterator.remove() during a loop | O(n) per call | O(1) per call |
| contains(), indexOf() | O(n) | O(n) |
| Memory per element | One array slot plus spare capacity | One node object with two links |
Removing or inserting in the middle is O(n) for both classes, because LinkedList must first walk to the position. The often-quoted O(1) insert for LinkedList applies only when we already stand at the position, i.e. at an end or on the current element of an iterator.
3. Why ArrayList Wins in Practice
Big O counts steps, but not what each step costs. Walking a LinkedList means following references to nodes spread across the heap, and each jump can miss the CPU cache. Shifting elements in an ArrayList is a single System.arraycopy() call over contiguous memory, which modern CPUs do very fast.
A LinkedList also creates one object per element, so it puts more work on the garbage collector, and it has no spare capacity to reuse. For these reasons, ArrayList is faster for most real workloads, even for inserts in the middle, because a LinkedList must walk the nodes to reach the position first. When performance matters, we measure both with JMH on our own data instead of relying on rules of thumb.
One pattern is slow with LinkedList no matter what. An index loop calls get(i) for every element, and each call walks the nodes, so the loop becomes O(n2).
static int totalLength(List<String> names) {
int total = 0;
for (String name : names) { // iterator, O(n) for both classes
total += name.length();
}
return total;
}
int fast = totalLength(new LinkedList<>(List.of("report", "invoice"))); // 13
Say a reporting service receives a List of 100,000 rows from a library that returns a LinkedList. An old for (int i = 0; …) loop with get(i) walks about 2.5 billion nodes in total, because each call starts again from the closer end. Switching to the enhanced for loop, as in totalLength(), makes the loop linear for every List implementation.
4. When LinkedList Is the Right Choice
LinkedList fits when we add and remove at both ends of a list, or remove many elements while walking a long list with an iterator. In both cases, no element shifts and no index lookup is needed.
LinkedList<String> pending = new LinkedList<>(List.of("report", "draft", "invoice", "draft"));
Iterator<String> it = pending.iterator();
while (it.hasNext()) {
if (it.next().equals("draft")) {
it.remove(); // unlinks one node, O(1)
}
}
String left = pending.toString(); // "[report, invoice]"
For bulk removal, ArrayList.removeIf() closes the gaps in one pass, so it is also linear. The LinkedList advantage is left for loops that make other decisions per element, such as inserting a new element with ListIterator.add() next to the current one.
As a queue or a stack, ArrayDeque is the better default. It is likely to be faster than LinkedList when used as a queue, because it uses a circular array instead of nodes. We keep LinkedList when we need null elements, which ArrayDeque rejects, or one object that is both a List and a Deque.
Deque<String> printer = new ArrayDeque<>();
printer.offerLast("report");
printer.offerLast("photo");
printer.offerFirst("urgent");
String printing = printer.pollFirst(); // "urgent"
String after = printer.peekFirst(); // "report"
A print server is the typical case. Jobs join at the tail, urgent jobs go to the head, and the printer always takes the head, so every operation touches an end and neither class needs an index.
5. Converting a LinkedList to an ArrayList
Both classes have a constructor that takes any Collection, so converting is one line. The constructor copies the elements in iteration order, and the two lists are independent afterwards.
LinkedList<String> linked = new LinkedList<>(List.of("report", "invoice", "photo"));
ArrayList<String> array = new ArrayList<>(linked); // [report, invoice, photo]
ArrayList<String> filled = new ArrayList<>();
boolean copied = filled.addAll(linked); // true, [report, invoice, photo]
ArrayList<String> upper = linked.stream().map(String::toUpperCase).collect(Collectors.toCollection(ArrayList::new)); // [REPORT, INVOICE, PHOTO]
List<String> readOnly = List.copyOf(linked); // [report, invoice, photo], unmodifiable
We use addAll() when the target list already exists, a stream when we transform elements on the way, and List.copyOf() when nobody should change the result. The reverse direction works the same way.
ArrayList<String> source = new ArrayList<>(List.of("report", "invoice"));
LinkedList<String> back = new LinkedList<>(source); // [report, invoice]
LinkedList<String> reversedCopy = new LinkedList<>(source.reversed()); // [invoice, report], Java 21
A common reason to convert is an API that returns a LinkedList, such as an older DAO, while our code reads elements by index. Copying once into an ArrayList costs O(n) and makes every later get(i) O(1).
6. Choosing Between ArrayList and LinkedList
For most code, the decision takes a few seconds. We start with ArrayList and switch only when the access pattern matches one of the rows below.
| Access pattern | Pick |
|---|---|
| Read by index, append at the end, loop over all elements | ArrayList |
| Sort, binary search or pass to APIs that index the list | ArrayList |
| Queue or stack without null elements | ArrayDeque |
| Add and remove at both ends and also need List methods | LinkedList |
| Insert or remove next to the current element while iterating a long list | LinkedList with ListIterator |
| Several threads share the list | A synchronized wrapper or CopyOnWriteArrayList |
Neither class is thread-safe, so the last row applies to both, and the tutorial on synchronizing an ArrayList shows the options. The List interface overview compares the remaining implementations.
7. LinkedList vs ArrayList FAQs
Developers comparing the two classes, in interviews or in code reviews, ask about speed, memory and queues.
7.1. Which Is Faster, ArrayList or LinkedList?
ArrayList for almost every workload. It reads by index in constant time, and its contiguous memory makes loops and shifts fast. LinkedList is faster only for repeated adds and removes at the head of a long list or through an iterator.
7.2. Why Is LinkedList Rarely Used?
Because ArrayList covers list use cases with less memory and ArrayDeque covers queue use cases faster. That leaves LinkedList for the few cases that need both a List and a Deque in one object or null elements in a queue.
7.3. Which Uses More Memory?
LinkedList. Every element needs its own node with two extra references and an object header, whereas ArrayList needs one array slot per element plus some spare capacity, which trimToSize() can release.
7.4. Does LinkedList Have a Capacity Like ArrayList?
No. LinkedList allocates one node per element, so it has no capacity, no ensureCapacity() and no resizing. It grows and shrinks one node at a time.
7.5. Is LinkedList Thread-Safe?
No, neither class is. For a shared queue, a ConcurrentLinkedDeque or a BlockingQueue such as LinkedBlockingQueue is the right tool, and for a shared list we use Collections.synchronizedList() or CopyOnWriteArrayList.
8. Conclusion
ArrayList keeps elements in one array and LinkedList in linked nodes. That one difference makes index access O(1) for ArrayList and O(n) for LinkedList, and it makes the ends of a LinkedList cheap to change.
In practice, ArrayList wins most comparisons because it uses less memory and works well with CPU caches. We keep LinkedList for iterator-based edits of long lists and for list-plus-deque use, choose ArrayDeque for queues and stacks, and convert between the classes with one constructor call whenever an API hands us the other type.
9. References
- LinkedList Javadoc (Java 25)
- ArrayList Javadoc (Java 25)
- ArrayDeque Javadoc (Java 25)
- Oracle Java Tutorials, List Implementations
Happy Learning !!