ArrayList Time Complexity: get, add, remove, contains
Fast recall
Quick revision
Core answer
ArrayList, get and set are O(1); end append is amortized O(1); insertion or removal near the middle is O(n) because references shift; contains and indexOf are O(n). A particular append that grows the backing array is O(n), and clearing or removing may also null stored references as part of the work.Precise knowledge of time complexity for common operations and the ability to explain why each operation has that complexity
Saying add is always O(1) without understanding the amortized qualifier or why middle insertion is O(n)
Explain the underlying reason for each complexity — array access for get, element shifting for middle insertion, resize copying for add-to-end
Interview-ready guide
Interview answer
ArrayList get and set are O(1) because the backing array supports direct indexed access. Adding at the end is amortized O(1): most appends use spare capacity, while an append that grows and copies the backing array is O(n). Inserting or removing near the middle is O(n) because later references shift. Contains and indexOf are O(n) because they compare elements in sequence.
I qualify end removal as a cheap special case because it has no long shift, and I do not hard-code an exact growth factor as an API promise. If frequent membership checks dominate, I consider a HashSet for expected O(1) lookup, provided I still meet ordering needs and keep any paired structures consistent.
ArrayList Time Complexity: get, add, remove, contains
Direct Answer
For ArrayList, get and set are O(1); end append is amortized O(1); insertion or removal near the middle is O(n) because references shift; contains and indexOf are O(n). A particular append that grows the backing array is O(n), and clearing or removing may also null stored references as part of the work.
Code Example
import java.util.ArrayList;
import java.util.List;
public class ArrayListComplexityDemo {
public static void main(String[] args) {
List<String> orders = new ArrayList<>();
orders.add("Order-1"); // amortized O(1) - write to next slot
orders.add("Order-2");
orders.add("Order-3");
String second = orders.get(1); // O(1) - direct array access
orders.add(1, "Order-1.5"); // O(n) - shift later elements right
// [Order-1, Order-1.5, Order-2, Order-3]
orders.remove(1); // O(n) - shift later elements left
// [Order-1, Order-2, Order-3]
boolean found = orders.contains("Order-2"); // O(n) - linear scan
int index = orders.indexOf("Order-3"); // O(n) worst case
}
}
Common Mistakes & Pitfalls
- Saying
addis always O(1): The precise term is amortized O(1) — any singleaddthat triggers a resize is O(n) because all elements get copied. "Amortized" means the average over many operations stays constant. - Forgetting the shift cost:
add(i, e)is not O(1). Every element from indexionward shifts right by one, which is O(n). - Using
ArrayListfor repeated membership checks: Looping and callingcontainseach time nests an outer loop with an inner linear scan — O(n^2). Use aHashSetfor expected O(1) membership when you check presence often.
Check your understanding
Answer these without looking back at the article.
- Why is removing from the middle of an ArrayList O(n)?
- When is ArrayList a poor choice as a data structure?
- How does ArrayList's contains compare to HashSet's contains?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.