Arrays and Index Positions in Java
list.size() returns the number of items in the list, which can be zero, and the valid positions are 0 through list.size()-1.
Why Positions Matter Before You Call Anything
An ArrayList is a dynamic array. An object of type ArrayList<T> has the instance methods you would expect from a dynamic array implementation, and four of them do most of the everyday work: size reports how many items the list holds, add puts a new item at the end, get reads the item at a given position, and set replaces the item at a given position. If you understand what each of these does to the list and which positions it accepts, you can read and write most ArrayList code with confidence.
Most ArrayList bugs come from using a position that does not exist. Before you learn the methods, test your intuition with a small question about a list that has three items.
What do you think happens?
A list holds three items. What does list.get(3) do?
Reveal answer
It throws an IndexOutOfBoundsException
A list of size 3 has valid positions 0, 1, and 2. Position 3 is outside that range, so get throws an IndexOutOfBoundsException. Position 3 looks like it has room, but no item is stored there.
Tracing a List from Empty
A call to the default constructor, new ArrayList<T>(), creates a list with size zero. The list holds no items yet, so it has no valid positions. Adding an item is the only thing that gives the list its first position. Follow the list step by step as the calls below run in order.
- The constructor creates a list of size zero.
- Each add call appends at the end and increases the size by 1.
- After two adds, the list holds apple at position 0 and banana at position 1.
Notice that you never chose a position when you called add. The new item simply became the last item, at the position that was list.size() before the call. After the call, that position is list.size()-1.
Reading the Size and the Valid Positions
The call list.size() returns the current size of the list, meaning the number of items in the list at that moment. The size changes as items are added or removed, so calling size later may give a different answer. The size can be zero, as in a brand-new list.
The size tells you where the valid positions end. The only valid positions are the whole numbers from 0 up to list.size()-1. Positions in an ArrayList start at 0, not 1. The first item is at position 0, the second is at position 1, and the last is at position list.size()-1. Because size() tells you exactly where the range ends, it is the standard way to check a position before you use it.
The diagram shows a list with three items. Its size is 3, so the valid positions are 0, 1, and 2. Position 3 does not exist, even though a list of size 3 seems to have room for it. Keeping this in mind prevents the most common off-by-one errors.
Finding the last valid position
A list has four items. Which positions are valid, and what is the last valid position?
- 1Read the size
list.size()is 4 because the list holds four items. - 2Apply the range rule
The valid positions run from 0 up to
list.size()-1, which is 4-1 = 3. - 3List the positions
The valid positions are 0, 1, 2, and 3. Position 4 is not valid because no item is stored there.
The valid positions are 0 through 3, and the last valid position is 3.
What add, get, and set Do
The call list.add(obj) appends the object obj to the end of the list and increases the size by 1. The parameter obj can be of type T or it can be null. Because add always appends, you do not choose a position when you call it.
The call list.get(N) returns the value stored at position N, and its return type is T. The integer N must lie in the range 0 to list.size()-1. Calling get is similar to reading A[N] from an array A, with one important difference: you can read with list.get(N), but you cannot use list.get(N) on the left side of an assignment statement.
The call list.set(N, obj) assigns obj to position N, replacing the item that was previously stored there. The parameter obj must be of type T, and N must be in the range 0 to list.size()-1. A call to set is equivalent to the array command A[N] = obj. Because set replaces an item instead of adding one, it does not change the size.
| Method | Changes the list? | Changes the size? | Accepted position | Purpose |
|---|---|---|---|---|
| size() | No | No | No position needed | Reports how many items there are |
| add(obj) | Yes, appends at the end | Yes, increases by 1 | No position chosen; goes to the end | Puts a new item at the end |
| get(N) | No | No | N from 0 to size()-1 | Reads the item at position N |
| set(N, obj) | Yes, replaces the item at N | No | N from 0 to size()-1 | Replaces an existing item |
Only add and set can change the list's contents, and only add changes the size.
Replacing an Item with set
The source example starts with a three-item list holding apple, banana, and cherry. A call to set at position 1 with the value blueberry replaces banana. Nothing else in the list changes, and the size remains 3. Only the item at position 1 is changed; the other two items keep their positions and values.
[apple, banana, cherry] size 3Position 1 holds banana
[apple, blueberry, cherry] size 3
Position 1 holds blueberry; size unchanged
Calls Outside the Valid Range
When get or set receives an index outside 0 to list.size()-1, an IndexOutOfBoundsException error occurs. You can check the outcome by comparing the index with the size before the call. The valid range depends on the current size, so a position that was valid earlier may be invalid after items are removed, and calling size again later may give a different range.
Tracing a Sequence of Calls
Now trace a full sequence. Start with an empty list, then run add, add, set, and get in order. After each step, state the size and the contents before reading the next line. The diagram shows the state after each call.
- After the first add, the list is [apple] with size 1.
- After the second add, the list is [apple, banana] with size 2.
- The set call replaces position 1 and leaves the size at 2.
- The get call reads position 0 and leaves the list unchanged.
Tracing add, set, and get
Starting from an empty list, what are the contents and size after add(apple), add(banana), set(1, blueberry), and then get(0) is called?
- 1Empty start
The list is created with new
ArrayList<String>(), so its size is 0 and it has no valid positions. - 2After add(apple)
The item apple is appended at position 0. The contents are [apple] and the size is 1.
- 3After add(banana)
banana is appended at position 1. The contents are [apple, banana] and the size is 2.
- 4After set(1, blueberry)
Position 1 is valid because 1 lies between 0 and
size()-1 = 1. The item banana is replaced by blueberry. The contents are [apple, blueberry] and the size stays 2. - 5After get(0)
Position 0 is valid. get returns apple and does not change the list, so the contents remain [apple, blueberry] and the size remains 2.
The list contains [apple, blueberry] with size 2, and get(0) returns apple.
Mistakes with Index Positions
Common mistake Starting counting positions at 1
What it looks likeA list holds apple and banana, and the code calls get(2) expecting banana.
Why it failsPositions in an
ArrayListstart at 0, so the first item is at 0 and the second is at 1.Do this insteadUse get(1) for banana, because the second item sits at position 1.
Common mistake Using list.size() as the last valid position
What it looks likeA list of size 4 is used with get(4) because 4 is the size.
Why it failsThe last valid position is
list.size()-1, which is 3 for a list of size 4.Do this insteadCheck that the index is at most list.size()-1 before calling get or set.
Common mistake Expecting set to add an item
What it looks likeA list of size 2 is used with set(2, cherry) expecting a third item to appear.
Why it failsset replaces an existing item and does not change the size. Position 2 is not valid for a list of size 2.
Do this insteadUse add(cherry) to append a new item at the end.
Common mistake Assigning the result of get on the left side
What it looks likelist.get(0) = blueberry is written to change the first item.
Why it failsget cannot be used on the left side of an assignment statement.
Do this insteadUse list.set(0, blueberry) to replace the item at position 0.
Common mistake Assuming size stays the same after each call; add increases it, while get and set leave the size unchanged
Practice Tracing a List
Start with an empty ArrayList<String> named list. Run these calls in order: add("cherry"), add("apple"), set(0, "blueberry"), add("banana"). State the contents and size after each call. Then say whether get(3) would be valid.
Hints
- After each add, the size goes up by exactly 1 and the new item sits at the end.
- set changes only the item at the given position and never changes the size.
- With size 3, the valid positions are 0, 1, and 2.
Key Takeaways
list.size()returns the number of items in the list at that moment, and it can be zero for a new list created with newArrayList<T>().- The valid positions are the whole numbers from 0 up to
list.size()-1, so a list of size 3 has positions 0, 1, and 2. add(obj)appends at the end and increases the size by 1;get(N)reads position N;set(N, obj)replaces position N and leaves the size unchanged.- get and set throw an
IndexOutOfBoundsExceptionwhen the position is outside the valid range, so check the index againstsize()before using it.