ArrayList
is one of the most frequently used classes in the Java Collections Framework. It is a resizable array implementation of the List
interface that supports dynamic growth, random access, and predictable performance characteristics. In real-world Java development, ArrayList
is the default go-to for ordered, index-based collections.
🔍 What is an ArrayList?
An ArrayList
is a dynamic array that automatically resizes itself when elements are added or removed. It allows duplicate elements and maintains the insertion order.
Key Characteristics:
- Ordered by index (like arrays)
- Allows duplicates and null values
- Resizable at runtime
- Backed by an internal array (
Object[]
)
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
System.out.println(names.get(1)); // Bob
🧠 How ArrayList Works Internally
Internal Structure
ArrayList
internally uses an Object[]
array. Here’s what happens under the hood:
- Default initial capacity is 10 (Java 8+)
- When capacity is exceeded, it resizes to 1.5x the previous size using
Arrays.copyOf()
transient Object[] elementData;
private static final int DEFAULT_CAPACITY = 10;
Resizing Logic
newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5x growth
Performance Trade-Off
- Resizing is costly as it involves array copy.
- Hence, prefer using
ensureCapacity()
when size is predictable.
⏱️ Performance Characteristics
Operation | Time Complexity |
---|---|
add(E e) |
O(1) amortized |
get(int index) |
O(1) |
remove(int index) |
O(n) |
contains(Object o) |
O(n) |
add(index, E e) |
O(n) |
✅ Common Use Cases
- Dynamic lists of user input or API responses
- Maintaining order while allowing duplicates
- Lists that grow and shrink frequently
Example:
List<Integer> scores = new ArrayList<>();
Collections.addAll(scores, 95, 88, 76, 88);
🧪 Practical Example with Streams
List<String> words = Arrays.asList("java", "spring", "boot");
List<String> upper = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upper); // [JAVA, SPRING, BOOT]
🔁 ArrayList vs LinkedList vs Vector
Feature | ArrayList | LinkedList | Vector |
---|---|---|---|
Resize Cost | Medium | None | Medium |
Access | Fast (O(1)) | Slow (O(n)) | Fast (O(1)) |
Thread-Safety | No | No | Yes (synchronized) |
Memory Overhead | Low | High | Low |
🚨 Common Pitfalls and Anti-patterns
❌ Adding while iterating (without iterator)
for (String s : list) {
list.remove(s); // Throws ConcurrentModificationException
}
✅ Fix:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
✨ Best Practices
- Use
initialCapacity
if size is known - Avoid frequent
remove()
from large lists - Use
Collections.unmodifiableList()
when immutability is required - Prefer
List.of()
for fixed-size unmodifiable lists (Java 9+)
List<String> fixed = List.of("A", "B", "C"); // Java 9
📌 What's New in Java Versions?
Java 8
- Introduced Streams API for functional programming
- Useful methods:
forEach()
,removeIf()
,replaceAll()
Java 9
List.of(...)
for immutable lists
Java 10
var
for local variable type inference
Java 11
- Minor performance improvements
Java 17
- Stable LTS for sealed classes, better GC
Java 21
- Further performance boosts, preview structured concurrency
🔄 Refactoring Legacy Code
Before:
List names = new ArrayList();
names.add("John");
String name = (String) names.get(0);
After (Java 8+):
List<String> names = new ArrayList<>();
names.add("John");
String name = names.get(0);
🧠 Real-World Analogy
Think of an ArrayList like a train with fixed compartments (array). When full, a new train with more compartments is created, and passengers are moved over. That "moving" is resizing.
❓ FAQ: Expert-Level Insights
-
What is the default capacity of an ArrayList?
- 10 (Java 8+)
-
Is ArrayList synchronized?
- No. Use
Collections.synchronizedList()
orCopyOnWriteArrayList
.
- No. Use
-
Can we store nulls?
- Yes, multiple
null
values are allowed.
- Yes, multiple
-
Why is remove(index) O(n)?
- Because it shifts all elements to the left after removal.
-
How to clone an ArrayList?
List<String> clone = new ArrayList<>(original);
-
What happens if capacity is exceeded?
- It resizes the array to 1.5x of current capacity.
-
Which is better: ArrayList or LinkedList?
- ArrayList for access-heavy operations; LinkedList for frequent insertions/removals.
-
How to make ArrayList immutable?
Collections.unmodifiableList(list)
orList.of(...)
-
How to avoid ConcurrentModificationException?
- Use
Iterator
orCopyOnWriteArrayList
- Use
-
Does ArrayList shrink automatically?
- No. Call
trimToSize()
manually.
- No. Call
🏁 Conclusion and Key Takeaways
- Use
ArrayList
when you need fast access, predictable order, and dynamic resizing. - Avoid it for frequent insertions/removals at the start/middle.
- Combine with Java 8+ features to make your code concise and expressive.
- Understand internals to write performant, maintainable code.
✨ Pro Tip: For small collections,
List.of()
(Java 9+) is your best friend — clean, immutable, and concise.