🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
java.lang.IndexOutOfBoundsException: Index: 5, Size: 3
at java.base/java.util.LinkedList.checkElementIndex(LinkedList.java:567)
at java.base/java.util.LinkedList.get(LinkedList.java:488)⚡ Quick Fix Works 80% of the time
Check bounds before positional access; prefer iteration over manual indexing.
if (index >= 0 && index < list.size()) {
T value = list.get(index);
}🧠 Why this Happens
Tap to expand the deep technical explanation
Lists expose positions 0..size()-1; anything else is undefined. AbstractList.checkElementIndex formats BOTH numbers into one message ("Index: 5, Size: 3") so the mistake is self-diagnosing. Crucially, valid insertion points for add(i, e) run 0..size INCLUSIVE while get/set stop at size-1 - that one-position gap between the two rules generates most of these crashes. Modern ArrayList routes through jdk.internal.util.Preconditions producing alternate wording; LinkedList keeps the classic formatter even on current JDKs.
The HITEC City Parking Spot Analogy:
Asking a three-story hotel for room number five: reception quotes your request and the building size together so you can spot the error instantly.
🔁 How to Reproduce Confirm this is your error
List<Integer> l = new LinkedList<>(List.of(1, 2, 3)); l.get(5); // IndexOutOfBoundsException: Index: 5, Size: 3 l.add(4, 9); // add allows index == size only, so this also throws
🛠️ Solutions (5 Ways to Fix)
Bounds-check loop variables before indexing
👉 Use this if indices come from parsing, pagination math, or external input.
One explicit range test converts a crash into handled input validation; do it where the index is born, not deep inside access code.
if (page < 0 || page >= pages.size()) {
return Page.EMPTY;
}
return pages.get(page);Guard last-element access against empty lists
👉 Use this if code reads get(size()-1) or get(0) routinely.
On an empty list, size()-1 becomes -1 and even get(0) explodes; isEmpty() checks (or Java 21 getLast()) make intent explicit.
if (!events.isEmpty()) {
Event latest = events.get(events.size() - 1);
}Know the add(i, e) insertion rule
👉 Use this if inserting at computed positions.
Valid insertion indices run 0..size INCLUSIVE (append allowed), unlike get/set which stop at size-1; clamp insertion points accordingly.
int pos = Math.min(rank, sorted.size()); // append if rank beyond end
sorted.add(pos, item);Prefer enhanced-for/streams over manual indexing
👉 Use this if loops iterate all elements anyway.
Removing hand-computed indexes removes off-by-one opportunities entirely; the compiler/stream machinery owns the cursor.
for (Row r : rows) process(r); // no index to get wrong
rows.forEach(this::process); // equivalentClamp subList-style windows
👉 Use this if slicing ranges computed from user paging requests.
Bounding lo/hi before slicing prevents both IOOBE and its sibling IllegalArgumentException from reversed ranges.
int lo = Math.max(0, offset);
int hi = Math.min(items.size(), offset + limit);
List<T> pageView = lo < hi ? items.subList(lo, hi) : List.of();📋 Version Notes
ALL List implementations print "Index: i, Size: n" style messages.
ArrayList and friends moved to Preconditions -> "Index 5 out of bounds for length 3"; LinkedList/ListIterator keep the classic wording (captured on current JDKs).
SequencedCollection adds getLast()/removeLast() as safe named alternatives to get(size()-1).
🛡️ How to Prevent This Next Time
Treat list positions as untrusted input: validate where produced, prefer iterator-based traversal, and remember add() has one extra legal position over get().