Unit 4 Data Collections in AP Computer Science A (Java): Arrays, ArrayLists, and 2D Arrays

What “Data Collections” Are and Why You Use Them

In Java, a data collection is any structure that lets you store many values and work with them as a group. If you only ever needed one test score, one name, or one sensor reading, a single variable would be enough. But most real programs deal with sets of related data: all scores in a class, all items in a shopping cart, all pixels in an image, all seats in a theater.

Data collections matter because they change how you design programs:

  • They let you write general solutions. Instead of 30 separate variables (score1, score2, …), you use one collection and loops.
  • They help you model the real world. A classroom has a list of students, not a fixed number of student variables.
  • They unlock standard algorithms: traverse, count, filter, search, sort, and aggregate.

In AP Computer Science A, the key “data collections” are:

  • Arrays: fixed-size, indexed storage of elements of one type.
  • ArrayLists: resizable, indexed lists (from the Java Collections Framework).
  • Often (and commonly tested in the course overall), 2D arrays: grid-like arrays of arrays.

A unifying idea across all of them is index-based access: you refer to elements by position (0, 1, 2, …). If you master indexing and traversal patterns, you can solve most “data collections” problems.

Values vs. references (why collections behave “differently”)

Arrays, ArrayLists, and 2D arrays are all objects in Java, which means variables store references to them (a kind of “address”), not the entire collection itself.

This matters when you assign or pass collections:

int[] a = {1, 2, 3};
int[] b = a;      // b refers to the same array as a
b[0] = 99;
System.out.println(a[0]); // prints 99

You didn’t copy the array—you copied the reference. This is called aliasing: two variables “alias” the same object.

That behavior is a common source of bugs, but it’s also powerful: you can write methods that modify a collection and see the changes outside the method.

Exam Focus
  • Typical question patterns
    • Predict output when arrays/ArrayLists are assigned to new variables (aliasing).
    • Trace a loop that traverses a collection and updates values.
    • Choose an appropriate structure (array vs. ArrayList) for a scenario.
  • Common mistakes
    • Assuming b = a; copies all elements (it does not).
    • Forgetting that collections are objects, so changes inside a method can affect the caller.
    • Mixing up “length” (arrays) and “size” (ArrayLists).

Arrays: Fixed-Size, Indexed Collections

An array is an object that stores a fixed number of elements, all of the same type, in a single indexed structure. You use arrays when:

  • You know the number of elements won’t change (or you’re okay with creating a new array if it does).
  • You need fast index-based access.
  • You want a simple structure with minimal overhead.

Core properties of arrays

  1. Fixed size: once created, the size cannot change.
  2. 0-based indexing: the first element is at index 0, last is at index length - 1.
  3. Homogeneous type: an int[] can only store int values; a String[] can only store String references.
  4. Direct access: arr[i] refers to the element at index i.

Creating arrays

There are two common ways to create an array.

1) Create an empty array with a length
int[] scores = new int[5];

This creates an array with 5 int elements. Because it’s “empty,” Java fills it with default values:

  • int defaults to 0
  • double defaults to 0.0
  • boolean defaults to false
  • reference types (like String) default to null

Example:

String[] names = new String[3];
System.out.println(names[0]); // prints null

A big idea: new String[3] creates the array, but not the String objects. Each element starts as null until you assign a real string.

2) Create and initialize with an array initializer
int[] scores = {90, 85, 100};

This creates an array of length 3 with those values.

Accessing and updating elements

You use bracket notation:

int x = scores[1];   // read
scores[1] = 88;      // write

If you use an index outside 0 to length - 1, Java throws an ArrayIndexOutOfBoundsException.

length (arrays) is a field, not a method

Arrays use .length (no parentheses):

int n = scores.length;

This differs from String.length() and ArrayList.size()—a frequent source of small but costly errors.

Arrays and references (aliasing in practice)

Because arrays are objects, this is aliasing:

int[] a = {1, 2, 3};
int[] b = a;

b[2] = 10;
System.out.println(a[2]); // 10

If you truly need an independent copy, you must copy elements into a new array (often via a loop). On the AP exam, you’re commonly expected to reason about aliasing even if you don’t use advanced copying utilities.

Worked example: building and updating an array

Suppose you want to curve all scores by adding 5 points, but not exceeding 100.

Step-by-step reasoning:

  • You need to update each element.
  • That means you must visit each index.
  • You also must cap values at 100.
int[] scores = {96, 70, 100, 88};

for (int i = 0; i < scores.length; i++) {
    scores[i] = Math.min(100, scores[i] + 5);
}

// scores is now {100, 75, 100, 93}

What goes wrong most often with arrays

  • Off-by-one errors: using i <= arr.length instead of i < arr.length.
  • Using .length(): arrays use .length.
  • Forgetting defaults: assuming new String[3] contains empty strings; it contains null.
Exam Focus
  • Typical question patterns
    • Trace array creation, default values, and updates using index-based loops.
    • Identify which indices are valid and whether an exception occurs.
    • Predict output from array aliasing.
  • Common mistakes
    • Using <= in loop bounds, causing out-of-bounds access.
    • Confusing .length with .length() or .size().
    • Assuming a newly created object array has real objects instead of null references.

Traversing Arrays and Writing Array Algorithms

Knowing how to create an array is only the beginning. The real power comes from traversal—systematically visiting elements to compute something or change the array.

Why traversal patterns matter

Most AP CSA collection problems reduce to one of these tasks:

  • Compute a total/average
  • Find a maximum/minimum
  • Count items matching a condition
  • Replace or transform elements
  • Compare neighboring elements
  • Detect a pattern (increasing sequence, duplicates, etc.)

If you can recognize which traversal pattern fits, you can write correct code faster and avoid common index errors.

Standard index-based traversal

The most common traversal uses a for loop:

for (int i = 0; i < arr.length; i++) {
    // use arr[i]
}

This pattern is ideal when:

  • You need the index (for example, comparing arr[i] to arr[i - 1]).
  • You need to update values (arr[i] = ...).
  • You want to traverse only part of the array.

Enhanced for loop (for-each) for arrays

Java also provides an enhanced for loop:

for (int x : arr) {
    // x is the value of the next element
}

This is best when you only need to read elements. A subtle but important point: for primitives like int, the loop variable x is a copy of the element value. Changing x does not change the array.

int[] a = {1, 2, 3};
for (int x : a) {
    x = x * 2;
}
System.out.println(a[0]); // still 1

If the array stores references to mutable objects, you can mutate the object through the reference, but you still can’t replace which object is stored at that index using the loop variable.

Common array algorithms (with explanations)

Below are core algorithm types you should be able to write and trace.

1) Accumulation (sum, total, average)

You keep a running total.

Key idea: initialize an accumulator variable before the loop, then update it for each element.

int[] nums = {2, 4, 6};
int sum = 0;
for (int i = 0; i < nums.length; i++) {
    sum += nums[i];
}
// sum is 12

What goes wrong:

  • Initializing sum inside the loop resets it every iteration.
  • Dividing integers when you meant to compute a decimal average (you may need double).
2) Find maximum/minimum

You track the “best so far.”

Key idea: initialize using the first element (when the array is non-empty), then compare.

int[] nums = {5, 1, 9, 3};
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
    if (nums[i] > max) {
        max = nums[i];
    }
}
// max is 9

What goes wrong:

  • Starting max at 0 fails if all numbers are negative.
  • Starting the loop at 0 works but is redundant; starting at 1 avoids comparing the first element to itself.
3) Counting with a condition

You count elements that satisfy a test.

int[] nums = {3, 10, 7, 10};
int count10 = 0;
for (int x : nums) {
    if (x == 10) {
        count10++;
    }
}
// count10 is 2

What goes wrong:

  • Using = instead of == (Java won’t allow it in an if condition for primitives, but students still get confused).
4) Filtering and building a result (often needs a second structure)

Arrays can’t resize, so “filtering” often means either:

  • Create a new array (if you know the size), or
  • Use an ArrayList (more flexible), or
  • Modify in place with careful shifting (harder).

On the AP exam, you might be asked to remove values from an array by shifting left and tracking the “logical size.” That requires careful index reasoning.

5) In-place replacement (map/transform)

You overwrite each element using its old value.

int[] nums = {1, 2, 3};
for (int i = 0; i < nums.length; i++) {
    nums[i] = nums[i] * nums[i];
}
// nums is now {1, 4, 9}

Worked example: shifting (a classic index trap)

Suppose you want to delete the element at index k from an array, shifting elements left. Because arrays can’t change size, you typically shift and then treat the last spot as “unused.”

Example:

  • Start with {4, 8, 2, 7}
  • Delete index 1 (value 8)
  • Shift left from index 1
int[] a = {4, 8, 2, 7};
int k = 1;

for (int i = k; i < a.length - 1; i++) {
    a[i] = a[i + 1];
}
// a is now {4, 2, 7, 7}

The last value is duplicated because you overwrote positions but didn’t “remove” the last slot (you can’t). In many AP-style problems, you would also keep a separate variable like size to represent how many elements are still considered valid.

Exam Focus
  • Typical question patterns
    • Write or trace loops that compute sums, counts, max/min, or replacements.
    • Determine whether an enhanced for loop correctly updates the collection.
    • Trace shifting logic for insertion/removal-like behavior.
  • Common mistakes
    • Trying to modify array contents via the enhanced for loop variable.
    • Incorrect loop bounds when comparing arr[i] and arr[i + 1].
    • Forgetting arrays can’t shrink, so “remove” requires shifting and/or tracking logical size.

ArrayLists: Resizable Lists with Index-Based Operations

An ArrayList is a resizable, ordered list of elements from the Java standard library (java.util). It behaves a lot like an array in that you can access elements by index—but it can grow and shrink as you add or remove items.

ArrayLists matter because many real problems involve collections whose size changes: a to-do list, a playlist, customers waiting in a queue, or items in a cart. In AP CSA, ArrayLists also show up often in free-response questions because they let you practice traversal and modification without worrying about fixed sizes.

Key properties of ArrayLists

  • Resizable: it can grow/shrink automatically.
  • 0-based indexing: same indexing rules as arrays.
  • Holds objects, not primitives: you store Integer, not int; Double, not double.
  • Shifting behavior: adding/removing at an index shifts later elements.

Declaring and creating an ArrayList

You must import it and specify a type parameter (the element type).

import java.util.ArrayList;

ArrayList<String> words = new ArrayList<String>();

Common AP CSA style allows (and you should recognize) the diamond operator:

ArrayList<String> words = new ArrayList<>();

Wrapper classes and autoboxing

Because ArrayLists store objects, Java uses wrapper classes for primitives:

  • intInteger
  • doubleDouble
  • booleanBoolean

With autoboxing, Java automatically wraps/unwraps in many cases:

ArrayList<Integer> nums = new ArrayList<>();
nums.add(5);              // autoboxes int -> Integer
int x = nums.get(0);      // unboxes Integer -> int

A misconception to avoid: autoboxing makes the code look like primitives are stored directly, but they are still objects under the hood.

Essential ArrayList methods (you must know these)

Assume ArrayList<E> list.

  • list.size() → number of elements
  • list.get(i) → element at index i
  • list.set(i, value) → replace element at index i and return the old value
  • list.add(value) → append to end
  • list.add(i, value) → insert at index i, shifting right
  • list.remove(i) → remove at index i, shifting left, returning removed element

These are the building blocks for almost every AP CSA ArrayList question.

Shifting: the hidden behavior behind add and remove

Understanding shifting prevents a lot of index bugs.

If you insert at index 0:

ArrayList<String> a = new ArrayList<>();
a.add("A");
a.add("B");
a.add(0, "X");
// list is now ["X", "A", "B"]

If you remove at index 1:

a.remove(1);
// list is now ["X", "B"]

Elements after the insertion/removal move to keep indices contiguous.

Traversing ArrayLists

There are three common traversal styles.

1) Standard forward index loop
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

Use this when you need indices or you might call set.

2) Enhanced for loop
for (String s : list) {
    System.out.println(s);
}

Use this for read-only traversal.

Be cautious: modifying the list structure (adding/removing) during an enhanced for loop is not allowed and typically causes a runtime error.

3) Backward index loop (especially for removals)

When you remove items while traversing, going backward avoids skipping elements.

Removing while traversing (the biggest ArrayList trap)

If you remove an element at index i while going forward, all later elements shift left. If you then increment i, you skip the new element that shifted into position i.

Example problem: remove all "hi" strings.

Incorrect (skips some):

for (int i = 0; i < list.size(); i++) {
    if (list.get(i).equals("hi")) {
        list.remove(i);
    }
}

Correct approaches:

1) Traverse backward:

for (int i = list.size() - 1; i >= 0; i--) {
    if (list.get(i).equals("hi")) {
        list.remove(i);
    }
}

2) Traverse forward, but only increment when you do not remove:

int i = 0;
while (i < list.size()) {
    if (list.get(i).equals("hi")) {
        list.remove(i);
    } else {
        i++;
    }
}

A very common overload pitfall: remove(int) vs remove(Object)

For ArrayList<Integer>, remove is tricky because int can mean an index.

ArrayList<Integer> nums = new ArrayList<>();
nums.add(10);
nums.add(20);
nums.add(10);

nums.remove(10); // tries to remove index 10 (crash if size < 11)

To remove the value 10, you need to pass an Integer object:

nums.remove(Integer.valueOf(10));

This is a classic AP CSA “why did this crash?” or “what does this do?” question.

Exam Focus
  • Typical question patterns
    • Trace code using add, add(index, ...), remove, set, get, and shifting effects.
    • Write loops that modify an ArrayList (remove/filter/replace).
    • Explain or fix a bug involving remove on ArrayList<Integer>.
  • Common mistakes
    • Using .length instead of .size().
    • Removing while traversing forward and unintentionally skipping elements.
    • Modifying an ArrayList inside an enhanced for loop.

ArrayList Algorithms: Searching, Filtering, and Replacing

Once you’re comfortable with ArrayList basics, you apply the same algorithmic thinking you used with arrays—but with a few extra considerations:

  • size() can change while your loop runs.
  • Insertions/removals shift indices.
  • Elements are objects, so equality often requires .equals().

Equality: == vs .equals() in collections

For primitives, == checks value equality. For objects, == checks whether two references are the exact same object.

In collections, you usually want logical equality, so you typically use .equals():

if (list.get(i).equals("AP")) { ... }

A common misconception is that == “works on strings.” Sometimes it appears to, due to string interning, but it is not reliable and is not what you should use for content comparison.

Linear search (finding an element)

The fundamental searching method in AP CSA contexts is linear search: look at each element until you find a match or reach the end.

Why it matters: it’s the default when the list is unsorted, and it’s easy to implement correctly.

public static int indexOfFirst(ArrayList<String> list, String target) {
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).equals(target)) {
            return i;
        }
    }
    return -1;
}

Key design choice: returning -1 when not found is a standard Java pattern (also used by many library methods).

Replacing based on a condition

Replacing means you keep the same size but update some elements.

Example: replace all negative numbers with 0.

public static void clampNegatives(ArrayList<Integer> nums) {
    for (int i = 0; i < nums.size(); i++) {
        if (nums.get(i) < 0) {
            nums.set(i, 0);
        }
    }
}

Using set is important: you cannot assign directly like nums[i] = 0 because ArrayList is not an array.

Filtering (removing based on a condition)

Filtering changes the size, so you must handle shifting.

Example: remove all values greater than 100.

public static void removeTooLarge(ArrayList<Integer> nums) {
    for (int i = nums.size() - 1; i >= 0; i--) {
        if (nums.get(i) > 100) {
            nums.remove(i);
        }
    }
}

Going backward avoids index-shift problems.

Combining algorithms: counting + conditional logic

Many FRQs ask you to compute something like “the number of times a pattern occurs” or “the longest run.” These are still traversals, but you keep extra state.

Example idea: count how many adjacent pairs are equal.

public static int countAdjacentMatches(ArrayList<String> list) {
    int count = 0;
    for (int i = 0; i < list.size() - 1; i++) {
        if (list.get(i).equals(list.get(i + 1))) {
            count++;
        }
    }
    return count;
}

Notice the loop bound: i < list.size() - 1 so that i + 1 stays valid.

Strings and collections: .equals and compareTo

When sorting or ordering strings, Java uses compareTo (lexicographic order). You don’t usually need to implement compareTo, but you should understand that sorting strings alphabetically relies on this ordering.

Exam Focus
  • Typical question patterns
    • Implement or trace linear search and return a sentinel value when not found.
    • Write code that removes items matching a condition without skipping elements.
    • Determine whether == vs .equals() is correct for collection elements.
  • Common mistakes
    • Using == to compare String values from a list.
    • Looping to i < list.size() while accessing i + 1.
    • Removing elements in a forward loop and skipping shifted elements.

Sorting Collections with Library Methods (and What You Can Conclude From Sorting)

Sorting puts elements into a defined order (for example, ascending numbers or alphabetical strings). In AP CSA, you’re typically expected to recognize and use Java’s library sorting tools and reason about the results.

Sorting matters because it enables simpler solutions to many tasks:

  • Finding minimum/maximum becomes trivial after sorting.
  • You can detect duplicates by checking neighbors.
  • You can compute medians (middle element(s)).
  • Many data processing tasks become easier once data is ordered.

Sorting ArrayLists with Collections.sort

To sort an ArrayList of objects that have a natural ordering (like String or Integer), you can use:

import java.util.ArrayList;
import java.util.Collections;

ArrayList<String> words = new ArrayList<>();
words.add("banana");
words.add("apple");
words.add("cherry");

Collections.sort(words);
System.out.println(words); // [apple, banana, cherry]

Key points:

  • The list is sorted in place (the same list object is rearranged).
  • The element type must be comparable in a natural way (common wrapper types and String are).

Sorting arrays with Arrays.sort

For arrays, Java provides Arrays.sort.

import java.util.Arrays;

int[] nums = {4, 1, 9, 2};
Arrays.sort(nums);
System.out.println(Arrays.toString(nums)); // [1, 2, 4, 9]

Arrays.toString is a convenient way to print arrays for debugging; you should recognize it if it appears.

What sorting changes (and what it doesn’t)

Sorting changes order, not membership:

  • Same elements, rearranged.
  • Size/length stays the same.

After sorting:

  • Minimum is at index 0.
  • Maximum is at index size - 1 (or length - 1).
  • Equal items become adjacent.

Worked example: using sorting to count distinct values

Suppose you have an ArrayList<Integer> and want to count how many distinct values appear.

Reasoning:

  • If the list is sorted, duplicates are adjacent.
  • You can count how many times the value changes as you traverse.
public static int countDistinct(ArrayList<Integer> nums) {
    if (nums.size() == 0) return 0;

    Collections.sort(nums);
    int distinct = 1;

    for (int i = 1; i < nums.size(); i++) {
        if (!nums.get(i).equals(nums.get(i - 1))) {
            distinct++;
        }
    }

    return distinct;
}

Two important notes:

  • This modifies the original list (because sorting is in place). On an exam, that side effect may matter.
  • For Integer, using .equals is correct.

Being careful about side effects

A common exam theme is whether a method changes the list passed in. Sorting will rearrange the list object, so if other parts of the program rely on the original order, sorting is a significant side effect.

If you needed to preserve original order, you’d make a copy first—but copying itself is not always emphasized in AP CSA questions. Still, you should be aware that “sort” is not a harmless read-only operation.

Exam Focus
  • Typical question patterns
    • Predict list/array contents after calling a sort method.
    • Use sorted order to reason about min/max, duplicates, or median positions.
    • Determine whether sorting changes the original collection (side effects).
  • Common mistakes
    • Forgetting to import Collections or Arrays in code snippets.
    • Assuming sorting returns a new list rather than modifying the existing one.
    • Using == instead of .equals() when checking for adjacent duplicates after sorting.

2D Arrays: Grids, Tables, and Nested Traversal

A 2D array in Java is an array whose elements are themselves arrays. It’s often used to model a grid or table, such as:

  • a seating chart (rows and columns)
  • a game board
  • an image (pixels)
  • daily temperatures over weeks (week × day)

Even if you think of it as “a rectangle,” Java represents it as “an array of rows.” That representation explains many 2D-array details that show up on AP questions.

Declaring and creating 2D arrays

Example: 3 rows, 4 columns:

int[][] grid = new int[3][4];

Default values apply just like 1D arrays (0 for int, null for references, etc.).

You can also initialize with nested braces:

int[][] grid = {
    {1, 2, 3},
    {4, 5, 6}
};

This has 2 rows and 3 columns.

length in 2D arrays

This is one of the most important ideas:

  • grid.length is the number of rows.
  • grid[r].length is the number of columns in row r.

Students often try grid[0].length for columns (which works for rectangular arrays), but grid[r].length is the more general and correct way to think about it.

Ragged arrays (rows can have different lengths)

Because each row is an array, row lengths can differ:

int[][] ragged = {
    {1, 2},
    {3, 4, 5},
    {6}
};

Here, ragged[1].length is 3, but ragged[2].length is 1.

This matters on the exam because you must use ragged[r].length in loops, not assume all rows have the same column count.

Traversing 2D arrays with nested loops

The standard traversal uses nested loops:

for (int r = 0; r < grid.length; r++) {
    for (int c = 0; c < grid[r].length; c++) {
        System.out.println(grid[r][c]);
    }
}

This reads row by row (often called “row-major order”).

Common 2D array algorithms

1) Sum all values
public static int sumAll(int[][] grid) {
    int sum = 0;
    for (int r = 0; r < grid.length; r++) {
        for (int c = 0; c < grid[r].length; c++) {
            sum += grid[r][c];
        }
    }
    return sum;
}
2) Count matches

Count how many times a value appears:

public static int countValue(int[][] grid, int target) {
    int count = 0;
    for (int r = 0; r < grid.length; r++) {
        for (int c = 0; c < grid[r].length; c++) {
            if (grid[r][c] == target) {
                count++;
            }
        }
    }
    return count;
}
3) Work with neighbors (grid logic)

Grid problems often involve checking adjacent cells (up/down/left/right). The key is to prevent out-of-bounds access by verifying row/column indices are in range before checking a neighbor.

For example, to check the cell to the right of (r, c), you must ensure c + 1 < grid[r].length.

Enhanced for loops with 2D arrays

You can use enhanced for loops, but you need two levels:

for (int[] row : grid) {
    for (int value : row) {
        System.out.println(value);
    }
}

This is convenient for read-only traversals and simple aggregations. If you need indices (for neighbor checks or writing back to a specific position), index-based loops are usually better.

What goes wrong in 2D arrays

  • Confusing rows and columns (grid.length vs grid[0].length).
  • Using grid[0].length for all rows in a ragged array.
  • Off-by-one errors in neighbor checks.
Exam Focus
  • Typical question patterns
    • Trace nested loops and determine which elements are visited (and in what order).
    • Write methods that compute totals/counts or modify a 2D array.
    • Reason about bounds checks for neighbors in a grid.
  • Common mistakes
    • Using the wrong length (rows vs columns).
    • Assuming rectangular shape when the array could be ragged.
    • Accessing neighbors without checking boundaries first.

Choosing Between Arrays and ArrayLists (and Recognizing Them in FRQs)

A big part of mastering data collections is choosing the right tool—and then applying the correct traversal/modification strategy.

When an array is a better fit

Use an array when:

  • The number of elements is fixed or known ahead of time.
  • You want simple, fast indexed access with minimal overhead.
  • You’re modeling something naturally fixed-size (days in a week, months in a year, a fixed set of exam scores).

A typical AP-style example is processing a fixed set of inputs stored in an array.

When an ArrayList is a better fit

Use an ArrayList when:

  • The size changes during the program.
  • You need to insert/remove items often.
  • You want convenient methods like add and remove.

A typical AP-style example is filtering a list of names based on a condition, removing items as you go.

Recognizing common FRQ patterns

AP CSA free-response questions frequently embed collection logic in a story context. The programming skills are the same:

  • “Compute a statistic”: sum/count/max/min traversal.
  • “Update all elements”: in-place transformation (arrays use arr[i] = ...; lists use set).
  • “Remove items that match a rule”: shifting logic (ArrayList removal patterns are especially common).
  • “Work with a grid”: nested loops and careful bounds.

Method design: parameters and side effects

Because arrays/ArrayLists are references, a method can modify the collection passed in.

Example: a method that doubles each element in an array will permanently change the caller’s array:

public static void doubleAll(int[] a) {
    for (int i = 0; i < a.length; i++) {
        a[i] *= 2;
    }
}

This is not “good” or “bad” by itself—it’s a design choice. But on exam questions, you must correctly predict whether modifications persist.

A mental model that helps

Think of:

  • an array as a row of fixed mailboxes (numbered 0 to length-1)
  • an ArrayList as a row of mailboxes that can be inserted/removed, causing mailbox numbers to be reassigned after the change

This explains why removing shifts indices and why you must be careful in loops.

Exam Focus
  • Typical question patterns
    • Decide whether code is correct for an array vs an ArrayList (length vs size, [] vs get/set).
    • Predict whether a method modifies the original collection.
    • Debug index-shift issues after insertions/removals.
  • Common mistakes
    • Writing list[i] or arr.get(i) (mixing syntax).
    • Forgetting that removing from an ArrayList shifts indices and changes size().
    • Assuming a method that receives a collection parameter works on a copy instead of the original.