Time Complexity of Collection Operations
Time complexity describes how the time an operation takes grows as a collection gets bigger. It is written in Big O notation: O(1) means the time stays the same however many elements there are, O(log n) means it grows very slowly, and O(n) means it grows in step with the number of elements. Knowing these costs is how you choose the right collection, and it is asked in nearly every Java interview. In this lesson, you will find the cost of the main operations for each collection, and see why each is what it is.
Big O in Plain Words
| Notation | Name | With 1,000,000 elements, roughly | Example |
|---|---|---|---|
| O(1) | Constant | 1 step | ArrayList.get(i), HashMap.get(k) |
| O(log n) | Logarithmic | 20 steps | TreeMap.get(k) |
| O(n) | Linear | 1,000,000 steps | ArrayList.contains(x) |
| O(n log n) | Linearithmic | 20,000,000 steps | Collections.sort(list) |
"Amortized O(1)" means usually O(1), with an occasional slow step (like resizing) whose cost, spread over many operations, is still constant on average.
Lists
| Operation | ArrayList | LinkedList |
|---|---|---|
get(i) / set(i, e) | O(1) | O(n) |
add(e) at the end | O(1) amortized | O(1) |
add(0, e) / remove(0) | O(n) | O(1) |
add(i, e) / remove(i) in the middle | O(n) | O(n) |
contains(e) / indexOf(e) | O(n) | O(n) |
| Iterating over all elements | O(n) | O(n) |
Sets
| Operation | HashSet / LinkedHashSet | TreeSet | EnumSet |
|---|---|---|---|
add | O(1) average | O(log n) | O(1) |
remove | O(1) average | O(log n) | O(1) |
contains | O(1) average | O(log n) | O(1) |
first / floor / ceiling | Not available | O(log n) | Not available |
Maps
| Operation | HashMap / LinkedHashMap | TreeMap | EnumMap |
|---|---|---|---|
put | O(1) average | O(log n) | O(1) |
get | O(1) average | O(log n) | O(1) |
remove | O(1) average | O(log n) | O(1) |
containsKey | O(1) average | O(log n) | O(1) |
containsValue | O(n) | O(n) | O(n) |
firstKey / floorKey | Not available | O(log n) | Not available |
HashMap is O(log n) in the worst case, when many keys collide in one bucket that has turned into a tree.
Queues and Deques
| Operation | ArrayDeque | PriorityQueue |
|---|---|---|
offer / add | O(1) amortized (either end) | O(log n) |
poll / remove the head | O(1) | O(log n) |
peek | O(1) | O(1) |
contains / remove(Object) | O(n) | O(n) |
Example: O(1) vs O(n) in Practice
Let's see an example that searches for values in a list and a set of the same size. Times vary between computers, so the output is a sample.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ContainsCost {
public static void main(String[] args) {
int n = 100_000;
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
list.add(i);
}
Set<Integer> set = new HashSet<>(list);
long start = System.nanoTime();
int found = 0;
for (int i = 0; i < 10_000; i++) {
if (list.contains(n - 1 - i)) found++;
}
long listMs = (System.nanoTime() - start) / 1_000_000;
start = System.nanoTime();
for (int i = 0; i < 10_000; i++) {
if (set.contains(n - 1 - i)) found++;
}
long setMs = (System.nanoTime() - start) / 1_000_000;
System.out.println("found " + found);
System.out.println("ArrayList.contains x 10,000: " + listMs + " ms");
System.out.println("HashSet.contains x 10,000: " + setMs + " ms");
}
}found 20000
ArrayList.contains x 10,000: 257 ms
HashSet.contains x 10,000: 0 msExplanation: Each ArrayList.contains scans the list from the start, about 100,000 comparisons for values near the end. HashSet.contains jumps straight to one bucket. With 10,000 searches, the difference is hundreds of times.
Common Mistakes
Calling list.contains inside a loop over another big list. That is O(n x m); use a HashSet for lookups.
Using LinkedList.get(i) in a loop. Each call is O(n), making the loop O(n squared).
Forgetting that containsValue on a map is O(n). Only key lookups are fast.
Interview Corner
What is the time complexity of HashMap get and put?
O(1) on average, because the key's hash leads straight to one bucket that usually holds zero or one entry. In the worst case, when many keys collide in one bucket, it is O(log n) since Java 8, because large buckets are converted to red-black trees (it was O(n) before Java 8).
Why is TreeMap O(log n)?
TreeMap stores its entries in a red-black tree, a balanced binary search tree whose height stays proportional to log n. Every get, put and remove walks from the root down one path of the tree, so it takes O(log n) steps.
Practice
- Easy: For each of these tasks, name the collection with the best cost: checking whether a username is taken; getting the 500th element; always serving the smallest job first.
- Harder: Find the common elements of two lists of 100,000 numbers, first with nested
containscalls, then with aHashSet. Time both and explain the difference using Big O.
Key Takeaways
Was this tutorial helpful?