🔴 The Error You're Seeing

Confirm this matches your console output. If it does, you're in the right place.

ERROR LOGjava.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)

Solution 1✓ Most common cause

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);
Solution 2

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); }
Solution 3

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);
Solution 4

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); // equivalent
Solution 5

Clamp 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

Java 8

ALL List implementations print "Index: i, Size: n" style messages.

Java 9+

ArrayList and friends moved to Preconditions -> "Index 5 out of bounds for length 3"; LinkedList/ListIterator keep the classic wording (captured on current JDKs).

Java 21

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().