Concepts / Arrays and Index Positions in Java

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.

  • Programming

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?

  • It returns the last item in the list

  • It returns null

  • It throws an IndexOutOfBoundsException

  • It adds a new item at position 3

Reveal answer
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.

java
Notes on the code
  • 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.

Position 0applePosition 1bananaPosition 2cherry
Which positions exist in a list of size 3, and which item sits at each position?

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.

Worked example

Finding the last valid position

A list has four items. Which positions are valid, and what is the last valid position?

  1. 1
    Read the size

    list.size() is 4 because the list holds four items.

  2. 2
    Apply the range rule

    The valid positions run from 0 up to list.size()-1, which is 4-1 = 3.

  3. 3
    List the positions

    The valid positions are 0, 1, 2, and 3. Position 4 is not valid because no item is stored there.

Result

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.

MethodChanges the list?Changes the size?Accepted positionPurpose
size()NoNoNo position neededReports how many items there are
add(obj)Yes, appends at the endYes, increases by 1No position chosen; goes to the endPuts a new item at the end
get(N)NoNoN from 0 to size()-1Reads the item at position N
set(N, obj)Yes, replaces the item at NNoN from 0 to size()-1Replaces 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.

Before
[apple, banana, cherry]  size 3

Position 1 holds banana

After

[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.

java
Notes on the code
  • 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.
Empty list[] size 0add(apple)[apple] size 1add(banana)[apple, banana] size 2set(1, blueberry)[apple, blueberry] size 2get(0)returns apple; listunchangedaddaddsetget
After each call in the sequence add, add, set, get, what are the list's contents and size?
Worked example

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?

  1. 1
    Empty start

    The list is created with new ArrayList<String>(), so its size is 0 and it has no valid positions.

  2. 2
    After add(apple)

    The item apple is appended at position 0. The contents are [apple] and the size is 1.

  3. 3
    After add(banana)

    banana is appended at position 1. The contents are [apple, banana] and the size is 2.

  4. 4
    After 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.

  5. 5
    After 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.

Result

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 like

    A list holds apple and banana, and the code calls get(2) expecting banana.

    Why it fails

    Positions in an ArrayList start at 0, so the first item is at 0 and the second is at 1.

    Do this instead

    Use 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 like

    A list of size 4 is used with get(4) because 4 is the size.

    Why it fails

    The last valid position is list.size()-1, which is 3 for a list of size 4.

    Do this instead

    Check 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 like

    A list of size 2 is used with set(2, cherry) expecting a third item to appear.

    Why it fails

    set replaces an existing item and does not change the size. Position 2 is not valid for a list of size 2.

    Do this instead

    Use add(cherry) to append a new item at the end.

  • Common mistake

    Assigning the result of get on the left side

    What it looks like

    list.get(0) = blueberry is written to change the first item.

    Why it fails

    get cannot be used on the left side of an assignment statement.

    Do this instead

    Use 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

EASY

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 new ArrayList<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 IndexOutOfBoundsException when the position is outside the valid range, so check the index against size() before using it.