Question
I usually write Java lists like this:
List<String> names = new ArrayList<>();
I use the List interface as the variable type so I can switch implementations later if needed.
In Java, when should LinkedList be used instead of ArrayList, and when is ArrayList the better choice?
Short Answer
By the end of this page, you will understand the practical differences between ArrayList and LinkedList in Java, including how they store data, how that affects performance, and which one is usually the better default. You will also learn the common operations where each structure performs well, the mistakes beginners make when choosing between them, and how this choice shows up in real Java codebases.
Concept
ArrayList and LinkedList are both Java implementations of the List interface, but they store data in very different ways.
ArrayList stores elements in a dynamic array.
- Elements are kept in contiguous memory-like array slots.
- Access by index is fast.
- Appending to the end is usually fast.
- Inserting or removing in the middle can be expensive because later elements must shift.
LinkedList stores elements as a chain of nodes.
- Each node stores the value and links to neighboring nodes.
- Access by index is slow because Java must walk through the chain.
- Adding or removing near the ends can be efficient.
- It has more memory overhead because each element needs node objects and links.
In real programming, the important question is not just "which one can do this operation faster in theory?" but also "what operations will my code do most often?"
As a rule of thumb:
- Use
ArrayListby default. - Use
LinkedListonly when you specifically need frequent insertions/removals at the beginning or through a list iterator, or when queue/deque-style operations are central.
Why ArrayList is usually the default:
- Fast random access with
get(index) - Better cache locality in practice
- Lower memory overhead
- Simpler and usually faster for most application code
Why LinkedList is less commonly chosen:
get(index)is slow:O(n)- Extra memory per element
- Real-world performance is often worse than beginners expect
- Many insert/remove scenarios still require traversal before the operation
Big-O summary:
| Operation | ArrayList | LinkedList |
|---|---|---|
get(index) | O(1) | O(n) |
| add at end | Amortized O(1) | O(1) |
| add/remove at start | O(n) | O(1) |
| add/remove in middle by index | O(n) | O(n) to reach node |
| iteration | O(n), usually very fast | O(n), often slower in practice |
So although LinkedList has some theoretical advantages, ArrayList wins for many real applications because most code reads items, loops through them, and accesses by index more often than it constantly inserts at the front.
Mental Model
Think of ArrayList as a row of numbered lockers.
- Every item has a position.
- If you want item 7, you go directly to locker 7.
- If you insert something in the middle, many later items need to shift one place.
Think of LinkedList as a treasure hunt with clues.
- Each item tells you where the next item is.
- To reach item 7, you must follow the chain from the start or end.
- Removing a known item from the chain is easy: reconnect the links.
So:
ArrayListis better when you want fast direct access.LinkedListis better when you are constantly linking and unlinking elements, especially at the ends.
Another short version:
ArrayListoptimizes finding by position.LinkedListoptimizes relinking nodes once you are already there.
Syntax and Examples
Both classes implement List, so you can write code against the interface.
Basic syntax
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
List<String> arrayNames = new ArrayList<>();
List<String> linkedNames = new LinkedList<>();
Example: ArrayList for general-purpose list usage
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Ava");
names.add("Liam");
names.add("Noah");
System.out.println(names.get(1));
System.out.println(names);
}
}
Why ArrayList fits here:
- Adding to the end is efficient
- Reading by index is fast
- This is the most common style in Java applications
Example: LinkedList for queue-like behavior
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
LinkedList<String> tasks = new LinkedList<>();
tasks.addLast("Send email");
tasks.addLast("Generate report");
tasks.addLast("Backup database");
System.out.println(tasks.removeFirst());
System.out.println(tasks);
}
}
Why LinkedList can fit here:
- Removing from the front is efficient
- Adding to the back is efficient
- It can act as a queue or deque
Important note
If you need queue behavior, prefer the queue interfaces where possible:
import java.util.ArrayDeque;
import java.util.Queue;
Queue<String> tasks = new ArrayDeque<>();
In many real projects, ArrayDeque is preferred over LinkedList for queue/deque operations.
Step by Step Execution
Consider this ArrayList example:
List<String> items = new ArrayList<>();
items.add("A");
items.add("B");
items.add("C");
items.add(1, "X");
System.out.println(items);
System.out.println(items.get(2));
Step by step:
-
itemsstarts empty.[]
-
items.add("A")Ais added at the end.[A]
-
items.add("B")Bis added at the end.[A, B]
-
items.add("C")Cis added at the end.[A, B, C]
-
items.add(1, "X")- Insert
Xat index1 - Existing elements from index
1onward shift right - Result:
[A, X, B, C]
- Insert
-
System.out.println(items)- Prints:
[A, X, B, C]
items.get(2)- Return the element at index
2 - That element is
B
- Return the element at index
B
Now compare with a LinkedList mental execution:
LinkedList<String> items = new LinkedList<>();
items.add("A");
items.add("B");
items.add("C");
System.out.println(items.get(2));
What happens conceptually:
- The list stores linked nodes
- To reach index
2, Java follows links through the list - It cannot jump directly like an array-backed list can
That is the key tradeoff: LinkedList may link/unlink efficiently, but indexed access is slower.
Real World Use Cases
When ArrayList is a good fit
ArrayList is the best choice for most list usage in Java applications:
- Returning a list of users from a service
- Storing search results
- Holding rows from a CSV file
- Collecting API response items
- Looping through values for validation or display
- Accessing items by index in UI or business logic
Example:
List<Product> products = new ArrayList<>();
This is common because most code:
- adds items,
- iterates over them,
- sometimes accesses by index,
- rarely inserts at the front repeatedly.
When LinkedList can be a good fit
Use LinkedList when your operations match its strengths:
- Frequent
addFirst()/removeFirst()operations - Frequent
addLast()/removeLast()operations - Deque-like behavior
- Insertion/removal through a
ListIteratorwhen already positioned
Example:
LinkedList<String> history = new LinkedList<>();
history.addFirst("/home");
history.addFirst("/products");
history.removeFirst();
A practical caution
If your use case is really a queue or stack, ArrayDeque is often better than LinkedList.
Example:
import java.util.ArrayDeque;
import java.util.Deque;
Deque<String> jobs = new ArrayDeque<>();
jobs.addLast("job1");
jobs.addLast("job2");
jobs.removeFirst();
Real Codebase Usage
In real Java codebases, developers usually follow these patterns:
1. Default to interfaces
List<String> names = new ArrayList<>();
This keeps code flexible and makes it easier to change implementations later.
2. Default to ArrayList
Most production code uses ArrayList unless there is a clear reason not to.
Why:
- It performs well for common workloads
- It is easier to reason about
- Most lists are read often and modified less dramatically than people assume
3. Use LinkedList only for specific access patterns
You may see it in code that:
- behaves like a deque
- adds/removes at both ends
- manipulates elements through iterators
Example pattern:
LinkedList<String> undoStack = new LinkedList<>();
undoStack.addFirst("state1");
undoStack.addFirst("state2");
String previous = undoStack.removeFirst();
4. Prefer purpose-specific interfaces
If the collection is used as a queue:
Queue<Task> queue = new ArrayDeque<>();
If it is used as a deque:
Deque<String> deque = new ArrayDeque<>();
This expresses intent better than using List for queue behavior.
5. Avoid premature optimization
Developers often choose LinkedList because they hear inserts are fast. But if the code must first search for the insertion point, the total work may still be slow.
Example:
- Finding the middle position: expensive in
LinkedList - Then inserting: cheap once found
- Overall result: often not better than
ArrayList
6. Measure when performance matters
In serious performance-sensitive code, developers benchmark with realistic data rather than relying only on Big-O rules.
Common Mistakes
1. Assuming LinkedList is always faster for insertions
This is one of the most common mistakes.
Broken reasoning:
- "Insertions are O(1), so
LinkedListmust be faster."
What is missed:
- You often need to traverse the list first
- That traversal is
O(n)
2. Using LinkedList when accessing by index often
Broken example:
List<String> names = new LinkedList<>();
for (int i = 0; i < names.size(); i++) {
System.out.println(names.get(i));
}
Why this is bad:
get(i)onLinkedListisO(n)- Repeating it in a loop can become very slow
Better:
for (String name : names) {
System.out.println(name);
}
Or use ArrayList if indexed access is important.
3. Choosing implementation before understanding usage
Beginners sometimes ask which class is better in general.
Better question:
- What operations happen most often?
- Read by index?
- Append?
- Remove from front?
- Iterate?
4. Using LinkedList as a queue when ArrayDeque is better
Possible but not ideal:
Queue<String> queue = new LinkedList<>();
Often better:
Queue<String> queue = new java.util.ArrayDeque<>();
5. Forgetting memory overhead
A LinkedList node stores extra links, so it usually uses more memory than ArrayList for the same elements.
6. Writing to the concrete type unnecessarily
Less flexible:
ArrayList<String> names = new ArrayList<>();
More flexible:
List<String> names = new ArrayList<>();
This does not mean you should switch implementations casually, but it keeps your code less coupled.
Comparisons
ArrayList vs LinkedList
| Feature | ArrayList | LinkedList |
|---|---|---|
| Backing structure | Dynamic array | Doubly linked nodes |
| Best for | General-purpose list usage | Frequent add/remove at ends |
| Access by index | Fast | Slow |
| Append to end | Usually fast | Fast |
| Insert at beginning | Slow | Fast |
| Remove from beginning | Slow | Fast |
| Memory usage | Lower | Higher |
| Iteration performance | Usually better | Often worse |
| Default choice | Yes | Rarely |
LinkedList vs ArrayDeque
| Use case | LinkedList | ArrayDeque |
|---|---|---|
| Queue operations | Works | Usually preferred |
| Deque operations | Works | Usually preferred |
| Random access | Poor | Not supported as a list |
| Memory/performance for queue use | Often worse | Usually better |
List interface vs concrete implementation
| Declaration style | Example | Why use it |
|---|---|---|
| Interface type | List<String> names = new ArrayList<>(); | Flexible, less coupled |
| Concrete type | ArrayList<String> names = new ArrayList<>(); | Use only when concrete methods matter |
Quick decision guide
| If you mostly... | Prefer |
|---|---|
| Read by index | ArrayList |
| Append items and iterate | ArrayList |
| Add/remove from the front often | LinkedList or ArrayDeque |
| Use queue/deque behavior | ArrayDeque |
| Need a general default | ArrayList |
Cheat Sheet
List<String> a = new ArrayList<>();
List<String> b = new LinkedList<>();
Choose ArrayList when
- You need
get(index)often - You mostly append items
- You iterate a lot
- You want the usual default choice
Choose LinkedList when
- You frequently add/remove at the start or end
- You need deque-like operations
- You are using a
ListIteratorto insert/remove while traversing
Performance summary
ArrayList.get(i)-> O(1)LinkedList.get(i)-> O(n)ArrayList.add(end)-> amortized O(1)LinkedList.addFirst()-> O(1)LinkedList.removeFirst()-> O(1)- Insert/remove in middle often requires traversal first
Best default
- Use
List<T> list = new ArrayList<>();
Better for queues/deques
- Prefer
ArrayDequeoverLinkedList
Common warning
This loop is bad for LinkedList:
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
Prefer:
for (String item : list) {
System.out.println(item);
}
FAQ
Should I use ArrayList or LinkedList by default in Java?
Use ArrayList by default. It is usually faster and more memory-efficient for common application code.
Is LinkedList ever better than ArrayList?
Yes. It can be a better fit when you frequently add or remove elements at the beginning or end, or when you use it as a deque.
Why is LinkedList.get(index) slow?
Because a linked list cannot jump directly to an index. It must follow links from one node to the next until it reaches that position.
Is LinkedList good for queues?
It works, but ArrayDeque is usually a better choice for queue and deque behavior.
Why do many developers write List<String> x = new ArrayList<>();?
Because programming to the interface keeps code less coupled to one implementation and easier to change later.
Is Big-O enough to choose between them?
No. Big-O is useful, but real performance also depends on memory layout, traversal costs, and actual usage patterns.
Does LinkedList use more memory than ArrayList?
Yes, usually. Each linked node stores extra references in addition to the element value.
When should I declare LinkedList<String> directly instead of List<String>?
Only when you need methods specific to LinkedList, such as addFirst() or removeLast().
Mini Project
Description
Build a small Java program that models a task processor and compares two kinds of collection usage: a general task list and a queue of pending jobs. This project demonstrates that ArrayList is a strong default for ordinary list storage, while queue-like operations are better expressed with a queue/deque structure rather than forcing everything into one list type.
Goal
Create a program that stores tasks in an ArrayList, processes waiting jobs with a deque, and prints the results clearly.
Requirements
- Create a general task list using the
Listinterface with anArrayListimplementation. - Add at least four task names and print one task by index.
- Create a pending job queue using
ArrayDeque. - Add at least three jobs to the queue and process them from the front.
- Print the remaining contents of both collections at the end.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.