Free for students · Ad-free · WCAG 2.1 AA Compliant · Accessibility

ArrayList Traversals

Lesson ~11 min read 8 MCQs

In simple terms: In simple terms, ArrayList traversal is about visiting each item in a list, one by one, to read, change, or remove it, just like checking off tasks on a to-do list.

Why this matters

Imagine you're the designated DJ for a road trip with your friends. You've got a huge ArrayList of songs, but you need to make some edits. Maybe you want to find all the songs by Taylor Swift, or maybe you need to remove all the songs that are less than two minutes long because they kill the vibe. How do you go through the entire playlist, song by song, to check each one? You can't just magically know where they are. You have to start at the beginning and systematically check every single entry.

This process of "walking through" a list is called traversal. It's a fundamental skill in programming. In this lesson, we'll master the different ways to traverse an ArrayList, learn how to safely modify a list as we go, and sidestep the common exceptions that trip up so many students.

Traversal: Systematically visiting each element in an ArrayList.

Concept overview

flowchart TD
    A[Start: i = list.size() - 1] --> B{i >= 0?};
    B -- Yes --> C{Is list.get(i) a value to remove?};
    B -- No --> G[End];
    C -- Yes --> D[list.remove(i)];
    C -- No --> E[i--];
    D --> E;
    E --> B;
This diagram shows a flowchart for safely removing elements from an ArrayList by traversing backward. The process starts at the last index, checks if the index is valid, then checks if the element at that index should be removed. If so, it removes the element before decrementing the index and looping again.

Core explanation

Hey everyone, it's Saavi. Let's talk about one of the most common things you'll ever do with an ArrayList: visiting every element inside it. This is called traversal.

Think of your ArrayList as a line of numbered mailboxes on a street. If you need to check every mailbox for a specific letter, you'd start at mailbox #0, then go to #1, then #2, and so on, until you reach the end. That's exactly what traversal is in code.

The Standard for Loop Traversal

The most classic and versatile way to traverse an ArrayList is with a standard for loop. It gives you precise control over the index.

Let's say we have an ArrayList of test scores and we want to print each one.

ArrayList<Integer> scores = new ArrayList<>();
scores.add(88);
scores.add(92);
scores.add(77);
scores.add(100);

// Start at index 0, go up to (but not including) the size, increment by 1
for (int i = 0; i < scores.size(); i++) {



<figure class="lesson-figure"><div class="shr-widget" data-shr-widget="{&quot;code&quot;:[&quot;ArrayList&lt;Integer&gt; scores = new ArrayList&lt;&gt;();&quot;,&quot;scores.add(88); scores.add(92); scores.add(77); scores.add(100);&quot;,&quot;&quot;,&quot;for (int i = 0; i &lt; scores.size(); i++) {&quot;,&quot;    System.out.println(\&quot;Score at index \&quot; + i + \&quot;: \&quot; + scores.get(i));&quot;,&quot;}&quot;],&quot;type&quot;:&quot;for_loop_trace&quot;,&quot;array&quot;:[88,92,77,100],&quot;trace_steps&quot;:[{&quot;line&quot;:4,&quot;vars&quot;:{&quot;i&quot;:0},&quot;output&quot;:&quot;Score at index 0: 88&quot;,&quot;highlight_cell&quot;:0},{&quot;line&quot;:4,&quot;vars&quot;:{&quot;i&quot;:1},&quot;output&quot;:&quot;Score at index 1: 92&quot;,&quot;highlight_cell&quot;:1},{&quot;line&quot;:4,&quot;vars&quot;:{&quot;i&quot;:2},&quot;output&quot;:&quot;Score at index 2: 77&quot;,&quot;highlight_cell&quot;:2},{&quot;line&quot;:4,&quot;vars&quot;:{&quot;i&quot;:3},&quot;output&quot;:&quot;Score at index 3: 100&quot;,&quot;highlight_cell&quot;:3}]}" aria-label="An animation showing a for loop iterating through an ArrayList of scores, highlighting the current index and updating the printed output."></div><figcaption class="lesson-figure-caption">Tracing a standard `for` loop accessing elements by index.</figcaption></figure>



  // Use .get(i) to access the element at the current index
  System.out.println("Score at index " + i + ": " + scores.get(i));
}

Output:

Score at index 0: 88
Score at index 1: 92
Score at index 2: 77
Score at index 3: 100

Notice the loop condition: i < scores.size(). We use < and not <=. This is critical. If our list has 4 elements, the indices are 0, 1, 2, and 3. scores.size() is 4. If we tried to access scores.get(4), the program would crash with an IndexOutOfBoundsException because that "mailbox" doesn't exist.

Comparing correct and incorrect loop conditions for `ArrayList` traversal.

The Enhanced for Loop (The "for-each" Loop)

If you only need to read the elements and don't care about their index, Java gives us a cleaner, simpler syntax: the enhanced for loop.

It’s like saying, "for each score in the scores list, do this."

ArrayList<String> friends = new ArrayList<>();
friends.add("Maya");
friends.add("Carlos");
friends.add("Priya");

// "For each String named 'friendName' in the 'friends' list..."
for (String friendName : friends) {
  System.out.println("Hello, " + friendName + "!");
}

Output:

Hello, Maya!
Hello, Carlos!
Hello, Priya!

It’s clean and less prone to off-by-one errors. But it has a major limitation...

The Big Trap: Modifying an ArrayList While Traversing

The Danger of the Enhanced for Loop

You should not add or remove elements from an ArrayList while traversing it with an enhanced for loop. If you try, Java will throw a ConcurrentModificationException.

Think of it this way: the enhanced for loop is like a tour guide who has a fixed itinerary. If you suddenly try to bulldoze one of the buildings on the tour (remove an element), the guide gets confused and immediately stops the tour to prevent further chaos. It's a safety feature.

So, if you need to modify the list, the enhanced for loop is not the right tool.

The for Loop Deletion Problem

"Okay, Saavi," you might say, "I'll just use a standard for loop to remove elements." Great idea! But there's a subtle trap here, too.

Let's try to remove all names shorter than 5 characters from a list.

ArrayList<String> names = new ArrayList<>();
names.add("Liam"); // length 4
names.add("Sofia"); // length 5
names.add("Noah"); // length 4
names.add("Aaliyah"); // length 7

// Let's try removing names shorter than 5 letters...
for (int i = 0; i < names.size(); i++) {
  if (names.get(i).length() < 5) {
    names.remove(i); // Uh oh...
  }
}
System.out.println(names); // [Sofia, Noah, Aaliyah]

Wait, "Noah" is still in the list! Why?

Let's trace it.

  1. i = 0: names.get(0) is "Liam". Its length is 4. We remove it. The list is now [Sofia, Noah, Aaliyah]. Crucially, "Sofia" is now at index 0, and "Noah" is at index 1.
  2. The loop increments i to 1.
  3. i = 1: The loop now checks names.get(1), which is "Noah". It completely skipped "Sofia" because "Sofia" slid down into the spot we just emptied.
  4. i = 1: names.get(1) is "Noah". Its length is 4. We remove it. The list is now [Sofia, Aaliyah].
  5. The loop increments i to 2.
  6. i = 2: The loop condition i < names.size() (is 2 < 2?) is now false. The loop ends.

The final list is [Sofia, Aaliyah]. We successfully removed "Liam" but our logic was flawed and we missed one. On the AP exam, this flawed logic would produce [Sofia, Noah, Aaliyah] because the second short name is skipped.

The Solutions for Safe Deletion

You have two reliable ways to solve this.

1. Traverse Backwards: If you go from the end of the list to the beginning, removing an element doesn't affect the indices of the elements you still need to visit.

for (int i = names.size() - 1; i >= 0; i--) {
  if (names.get(i).length() < 5) {
    names.remove(i);
  }
}
System.out.println(names); // [Sofia, Aaliyah] -> This works!

2. Adjust the Index When Traversing Forward: If you must go forward, you can manually correct for the shift. When you remove an element at index i, the next element slides into index i. To avoid skipping it, you need to decrement i so the loop re-evaluates the same index on the next iteration.

for (int i = 0; i < names.size(); i++) {
  if (names.get(i).length() < 5) {
    names.remove(i);
    i--; // Counteract the i++ in the loop header
  }
}
System.out.println(names); // [Sofia, Aaliyah] -> This also works!

While both are correct, traversing backwards is generally considered cleaner and less error-prone. It's my go-to recommendation.

Tracing an enhanced `for` loop, iterating directly over elements.
Why modifying an `ArrayList` during an enhanced `for` loop is dangerous.

See it in action

ArrayList
Java

    
Op 0 / 0

Worked examples

Let's walk through a couple of common scenarios you'll face on the AP exam.

Example 1

Calculating the Average

Problem: You're given an ArrayList of Integer objects representing the daily high temperatures in Boston for a week. Write a method calculateAverage that takes this list and returns the average temperature as a double.

Solution Walkthrough:

  1. 1
    The Goal
    To find the average, we need the sum of all temperatures and the total number of days.
  2. 2
    The Traversal
    We need to visit every element to add it to a running total. Since we are only reading the data, we can use a simple enhanced for-each loop.
  3. 3
    The Code
    public double calculateAverage(ArrayList<Integer> temps) {
      // If the list is empty, the average is 0 to avoid division by zero.
      if (temps.size() == 0) {
        return 0.0;
      }
    
      double sum = 0.0; // Use a double for the sum to maintain precision.
    
      // Use an enhanced for-loop for simple, read-only traversal.
      for (Integer temp : temps) {
        sum += temp; // Add each temperature to the sum.
      }
    
      // The average is the sum divided by the number of elements.
      return sum / temps.size();
    }
  4. 4
    Why this works
    The enhanced for-loop is perfect here. It's readable and handles the iteration for us. We initialize a sum to zero, loop through each temperature, add it to the sum, and finally, divide by the list's size. We use a double for the sum to prevent integer division, which would truncate any decimal part.
Example 2

Removing Failing Grades

Problem: A teacher, Mr. Davis, has an ArrayList of student scores. He wants to remove all scores below 60. Write a method removeFailing that modifies the list in place.

Solution Walkthrough:

  1. 1
    The Goal
    Traverse a list and remove elements that meet a certain condition (score < 60).
  2. 2
    The Challenge
    We are modifying the list while traversing. This means an enhanced for-loop is out, and a standard for loop needs to be handled carefully.
  3. 3
    The Wrong Move
    A common mistake is to write a simple forward loop without adjusting the index. As we saw, this skips elements after a removal.
  4. 4
    The Correct Approach (Traversing Backwards)
    This is the safest and cleanest way. By starting at the end, removing an element at index i doesn't change the index of any elements at i-1, i-2, etc., which are the ones we still have to check.
    public void removeFailing(ArrayList<Integer> scores) {
      // Traverse backwards from the end of the list to the beginning.
      for (int i = scores.size() - 1; i >= 0; i--) {
        // Check the condition for removal.
        if (scores.get(i) < 60) {
          // Remove the element at the current index.
          scores.remove(i);
        }
      }
    }
  5. 5
    Why this works
    Let's say the list is [85, 45, 90, 59].
    • i = 3: scores.get(3) is 59. It's < 60. Remove it. List is now [85, 45, 90].
    • i = 2: scores.get(2) is 90. It's not < 60. Do nothing.
    • i = 1: scores.get(1) is 45. It's < 60. Remove it. List is now [85, 90].
    • i = 0: scores.get(0) is 85. It's not < 60. Do nothing. The loop finishes, and the list is correctly modified to [85, 90]. No elements were skipped.
Removing elements safely by traversing backwards.

Try it yourself

Ready to try a couple on your own? Think through the logic, especially if you need to modify the list.

  1. 1
    Word Filter
    Write a method filterLongWords that takes an ArrayList<String> and an int minLength. It should return a new ArrayList<String> containing only the words from the original list that are minLength or longer.
    • Hint: Since you're creating a new list, you don't have to worry about the tricky removal logic. A simple traversal to read from the old list and add to the new one is perfect. Which type of loop is cleanest for that?
  2. 2
    In-Place Doubler
    Write a method doubleValues that takes an ArrayList<Integer>. The method should modify the list in place, replacing every value with two times its original value. For example, [1, 5, 10] should become [2, 10, 20].
    • Hint: You need to change the elements, but not add or remove them. This means you need access to the index to use the set(index, value) method. Which loop gives you easy access to the index?
Doubling values in-place using a standard `for` loop and `set()`.