AP CSA Unit 4 Study Guide (2026) - Arrays, ArrayList, 2D Arrays and Sorting

AP Computer Science A

Unit 4: Data Collections

Ultimate Study Guide - 2025-2026 Curriculum

30-40% of AP Exam | FRQ Questions 3 & 4

Unit 4 Overview

Unit 4: Data Collections is the largest and most important unit on the AP CSA Exam. It covers everything from basic arrays to complex algorithms, and accounts for nearly 40% of your exam score.

Exam Weight: 30-40% of Multiple Choice

Plus TWO of the four Free Response Questions:

  • FRQ #3: Data Analysis with ArrayList
  • FRQ #4: 2D Array — nested loops, row/column processing

Unit 4 content appears in roughly 50% of your total exam score!

What's New for 2025-2026

NEW Topic: File I/O with Scanner

The College Board added reading from text files using the File and Scanner classes. You'll need to know: hasNext(), nextLine(), nextInt(), next(), the throws IOException clause, and processing file data into arrays or ArrayLists.

Unit 4 Topic Breakdown

Topic Content Exam Focus
4.1 Ethical & Social Issues Around Data Collection Conceptual MCQ
4.2 Introduction to Using Data Sets Conceptual MCQ
4.3 Array Creation and Access MCQ, FRQ 3 & 4
4.4 Array Traversals MCQ, FRQ 3 & 4
4.5 Implementing Array Algorithms MCQ, FRQ 3 & 4
4.6 Using Text Files NEW MCQ, possibly FRQ
4.7 Wrapper Classes (Integer, Double) MCQ, FRQ 3
4.8 ArrayList Methods Heavy MCQ, FRQ 3
4.9 ArrayList Traversals Heavy MCQ, FRQ 3
4.10 Implementing ArrayList Algorithms Heavy MCQ, FRQ 3
4.11 2D Array Creation and Access Heavy MCQ, FRQ 4
4.12 2D Array Traversals Heavy MCQ, FRQ 4
4.13 Implementing 2D Array Algorithms Heavy MCQ, FRQ 4
4.14 Searching Algorithms MCQ (tracing)
4.15 Sorting Algorithms MCQ (tracing)
4.16 Recursion MCQ (tracing)
4.17 Recursive Searching and Sorting MCQ (tracing)

Arrays

An array is a container that holds a fixed number of values of a single type. Once created, an array's size cannot change. Arrays are the foundation of data collections in Java.

Creating Arrays

Method 1: Declare Size (Elements Get Default Values)

// Create array of 5 integers - all initialized to 0
int[] numbers = new int[5];
// numbers = [0, 0, 0, 0, 0]

// Create array of 3 Strings - all initialized to null
String[] names = new String[3];
// names = [null, null, null]

// Create array of 4 doubles - all initialized to 0.0
double[] prices = new double[4];

Method 2: Initialize with Values (Size Inferred)

int[] scores = {85, 90, 78, 92, 88};   // length = 5
String[] colors = {"red", "green", "blue"}; // length = 3

Default Values by Type

Element Type Default Value Example
int 0 new int[3] → [0, 0, 0]
double 0.0 new double[2] → [0.0, 0.0]
boolean false new boolean[2] → [false, false]
String / any object null new String[2] → [null, null]

Accessing and Modifying Elements

int[] arr = {10, 20, 30, 40, 50};

// Access elements using index (0-based!)
System.out.println(arr[0]);              // 10  (first element)
System.out.println(arr[2]);              // 30  (third element)
System.out.println(arr[arr.length - 1]);  // 50  (last element)

// Get array length (no parentheses - it's a field, not a method)
System.out.println(arr.length);  // 5

// Modify elements
arr[0] = 100;              // [100, 20, 30, 40, 50]
arr[4] = arr[3] + 10;     // [100, 20, 30, 40, 50]

Array Index Diagram

//   Index:    0     1     2     3     4
//          +-----+-----+-----+-----+-----+
//   arr:   | 10  | 20  | 30  | 40  | 50  |
//          +-----+-----+-----+-----+-----+
//
//   arr.length = 5
//   Valid indices: 0 through arr.length - 1  (0 through 4)
//   First element: arr[0]
//   Last element:  arr[arr.length - 1]  =  arr[4]
ArrayIndexOutOfBoundsException

Accessing index < 0 or ≥ length causes a runtime crash. The #1 runtime error on the AP exam. Always check your loop bounds!

int[] arr = {10, 20, 30};  // valid indices: 0, 1, 2
arr[3];          // CRASH! Index 3 doesn't exist
arr[-1];         // CRASH! Negative indices don't exist
arr[arr.length]; // CRASH! arr.length = 3, so arr[3] is out of bounds

Arrays of Objects

// Array of String objects - initialized to null, NOT empty strings!
String[] words = new String[3];
// words = [null, null, null]

words[0] = "Hello";
words[1] = "World";
// words = ["Hello", "World", null]

// Calling a method on null causes NullPointerException!
if (words[2] != null)               // Always null-check first
    System.out.println(words[2].length());  // Safe
Key Array Facts for the Exam
  • Size is FIXED once created — you cannot add or remove elements
  • Use arr.length (no parentheses) for size
  • Indices go from 0 to length - 1
  • Object arrays initialize to null, not empty objects
  • Primitive arrays (int, double, boolean) get 0, 0.0, false
Assignment Does NOT Copy an Array!
int[] a = {1, 2, 3};
int[] b = a;  // b points to the SAME array as a (alias!)

b[0] = 99;
System.out.println(a[0]);  // 99 — a was also changed!

// To make an independent copy, loop and copy each element:
int[] copy = new int[a.length];
for (int i = 0; i < a.length; i++)
    copy[i] = a[i];

Array Traversals

Traversing an array means visiting each element to read, process, or modify its value. There are five traversal patterns you need to know for the AP exam.

Pattern 1: Standard for Loop

Use when you need the index, need to modify elements, or need to compare adjacent elements.

int[] arr = {10, 20, 30, 40, 50};

for (int i = 0; i < arr.length; i++)
    System.out.println("Index " + i + ": " + arr[i]);
// Index 0: 10  |  Index 1: 20  |  ...  |  Index 4: 50
Loop Bounds: Use < not <=
for (int i = 0; i < arr.length; i++)   // ✔ Correct: 0 to length-1
for (int i = 0; i <= arr.length; i++)  // ✖ Crash: tries arr[length]

Pattern 2: Enhanced for Loop (for-each)

Use when you only need to read values and don't need the index. Cleaner syntax, but cannot modify.

for (int num : arr)
    System.out.println(num);
// Output: 10, 20, 30, 40, 50 (each on new line)

// Works with String arrays too:
String[] words = {"apple", "banana", "cherry"};
for (String word : words)
    System.out.println(word.toUpperCase());
// APPLE  BANANA  CHERRY
Enhanced for Loop Cannot Modify the Array!
int[] arr = {10, 20, 30};
for (int num : arr)
    num = num * 2;  // Only changes local copy — array UNCHANGED
// arr is still [10, 20, 30]

// To modify, use a standard for loop:
for (int i = 0; i < arr.length; i++)
    arr[i] = arr[i] * 2;  // This DOES modify: [20, 40, 60]

Pattern 3: Backward Traversal

Essential for removing elements from ArrayList while traversing.

for (int i = arr.length - 1; i >= 0; i--)
    System.out.println(arr[i]);
// Output: 50, 40, 30, 20, 10

Pattern 4: Partial Traversal

int[] arr = {10, 20, 30, 40, 50, 60};

// First half only
for (int i = 0; i < arr.length / 2; i++)
    System.out.print(arr[i] + " ");  // 10 20 30

// Every other element (even indices)
for (int i = 0; i < arr.length; i += 2)
    System.out.print(arr[i] + " ");  // 10 30 50

// Every other element (odd indices)
for (int i = 1; i < arr.length; i += 2)
    System.out.print(arr[i] + " ");  // 20 40 60

Pattern 5: Accessing Adjacent Elements

Used to compare neighbors — requires careful bounds to avoid crashing.

int[] arr = {10, 20, 30, 40, 50};

// Compare each element with the NEXT one (stop at length - 1!)
for (int i = 0; i < arr.length - 1; i++)
{
    if (arr[i] > arr[i + 1])
        System.out.println(arr[i] + " > " + arr[i + 1]);
}

// Compare each element with the PREVIOUS one (start at 1!)
for (int i = 1; i < arr.length; i++)
{
    if (arr[i] < arr[i - 1])
        System.out.println(arr[i] + " < " + arr[i - 1]);
}
When to Use Which Loop?
Situation Use
Just reading values Enhanced for loop
Need the index Standard for loop
Modifying elements Standard for loop
Comparing adjacent elements Standard for loop
Traversing backward Standard for loop
Skipping elements (every other) Standard for loop

Array Algorithms

These are the standard algorithms the AP exam tests most heavily. Memorize every pattern — they appear in both MCQ tracing and FRQ writing.

Algorithm 1: Sum of Elements

int[] arr = {10, 20, 30, 40, 50};
int sum = 0;  // accumulator starts at 0
for (int num : arr)
    sum += num;
System.out.println(sum);  // 150

Algorithm 2: Average of Elements

int sum = 0;
for (int num : arr) sum += num;

// MUST cast to double before dividing - otherwise integer division!
double average = (double) sum / arr.length;
// Without cast: 150 / 5 = 30  (truncates)
// With cast:    150.0 / 5 = 30.0  (correct)

Algorithm 3: Find Maximum

int[] arr = {23, 45, 12, 67, 34};

// Initialize to FIRST element, never to 0
int max = arr[0];
for (int i = 1; i < arr.length; i++)  // start at 1
    if (arr[i] > max) max = arr[i];
System.out.println(max);  // 67
Never Initialize max to 0!
int[] arr = {-5, -3, -8, -1};
int max = 0;     // WRONG: 0 isn't even in the array!
int max = arr[0]; // ✔ Correct: -5, updates to -1

Algorithm 4: Find Minimum

int min = arr[0];
for (int i = 1; i < arr.length; i++)
    if (arr[i] < min) min = arr[i];  // just flip > to <

Algorithm 5: Count Occurrences

int[] arr = {5, 3, 5, 7, 5, 2};
int target = 5;
int count = 0;
for (int num : arr)
    if (num == target) count++;
System.out.println(count);  // 3

Algorithm 6: Count Elements Meeting a Condition

int[] arr = {12, 7, 25, 3, 18, 9};

int evenCount = 0;
for (int num : arr)
    if (num % 2 == 0) evenCount++;  // 2 (12 and 18)

int bigCount = 0;
for (int num : arr)
    if (num > 10) bigCount++;  // 3 (12, 25, 18)

Algorithm 7: Check if ANY Element Meets a Condition

// Start false, flip to true if you find one match
boolean hasNegative = false;
for (int num : arr)
    if (num < 0) hasNegative = true;

Algorithm 8: Check if ALL Elements Meet a Condition

// Start true, flip to false if any element fails
boolean allPositive = true;
for (int num : arr)
    if (num <= 0) allPositive = false;

Algorithm 9: Shift Elements Left

int[] arr = {1, 2, 3, 4, 5};
// Traverse FORWARD: each element gets the value of the next one
for (int i = 0; i < arr.length - 1; i++)
    arr[i] = arr[i + 1];
arr[arr.length - 1] = 0;  // clear last position
// arr = [2, 3, 4, 5, 0]

Algorithm 10: Shift Elements Right

int[] arr = {1, 2, 3, 4, 5};
// Traverse BACKWARD: each element gets the value of the previous one
for (int i = arr.length - 1; i > 0; i--)
    arr[i] = arr[i - 1];
arr[0] = 0;  // clear first position
// arr = [0, 1, 2, 3, 4]
Shift Direction = Traversal Direction

Shifting left? Traverse forward (overwrite each element with its right neighbor). Shifting right? Traverse backward (overwrite each element with its left neighbor). If you go the wrong direction you clobber data before you use it.

Algorithm 11: Reverse an Array

int[] arr = {1, 2, 3, 4, 5};
// Swap elements from outside in, only go halfway
for (int i = 0; i < arr.length / 2; i++)
{
    int temp = arr[i];
    arr[i] = arr[arr.length - 1 - i];
    arr[arr.length - 1 - i] = temp;
}
// Trace: swap arr[0]&arr[4] → [5,2,3,4,1]
//        swap arr[1]&arr[3] → [5,4,3,2,1]
//        i=2 is NOT < 5/2=2, loop ends
// arr = [5, 4, 3, 2, 1]

Algorithm 12: Copy an Array

int[] original = {1, 2, 3, 4, 5};
int[] copy = new int[original.length];
for (int i = 0; i < original.length; i++)
    copy[i] = original[i];

copy[0] = 99;
// copy = [99, 2, 3, 4, 5]
// original = [1, 2, 3, 4, 5] (unchanged)

Using Text Files NEW for 2025-2026

The College Board added file I/O to the curriculum. Use the File and Scanner classes to read data from text files.

Basic File Reading Pattern

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public static void readData() throws IOException
{
    File myFile = new File("data.txt");
    Scanner input = new Scanner(myFile);
    while (input.hasNext())
    {
        String line = input.nextLine();
        System.out.println(line);
    }
    input.close();
}
throws IOException is REQUIRED

Any method that reads from a file must include throws IOException in its header.

Scanner Methods for Files

Method Returns Description
hasNext() boolean True if more data to read
nextLine() String Reads entire next line
next() String Reads next word
nextInt() int Reads next integer
nextDouble() double Reads next double

Reading Numbers into an ArrayList

public static ArrayList<Integer> loadNumbers(String filename) throws IOException
{
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    Scanner input = new Scanner(new File(filename));
    while (input.hasNext())
        numbers.add(input.nextInt());  // autoboxing: int → Integer
    input.close();
    return numbers;
}
📄Practice Questions1 link

Wrapper Classes

ArrayLists can only hold objects, not primitive types. Wrapper classes wrap each primitive in an object so it can be stored in an ArrayList.

Primitive Types and Their Wrappers

Primitive Wrapper Class Create Manually
int Integer Integer.valueOf(5)
double Double Double.valueOf(3.14)
boolean Boolean Boolean.valueOf(true)

Autoboxing: Automatic Primitive → Object

// Java automatically converts primitives to wrapper objects
Integer num = 42;    // int 42 is "boxed" into an Integer object
Double price = 9.99; // double is boxed into Double

// Autoboxing happens automatically when adding to ArrayList
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(10);  // int 10 autoboxed to Integer
numbers.add(20);
numbers.add(30);

Unboxing: Automatic Object → Primitive

Integer numObj = 42;
int numPrim = numObj;  // Integer is automatically "unboxed" to int

// Unboxing happens automatically when getting from ArrayList
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(100);
int value = numbers.get(0);       // Integer unboxed to int
int doubled = numbers.get(0) * 2; // unboxing happens automatically
NullPointerException with Unboxing
Integer num = null;
int x = num + 5;  // CRASH! Can't unbox null

// Can also happen with ArrayList:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(null);          // legal - ArrayList can store null
int val = list.get(0);  // CRASH! Unboxing null

Useful Integer Methods

// Convert String to int
int num = Integer.parseInt("123");  // 123

// Maximum and minimum int values
int maxInt = Integer.MAX_VALUE;  // 2147483647
int minInt = Integer.MIN_VALUE;  // -2147483648

// Convert String to double
double pi = Double.parseDouble("3.14159");  // 3.14159
When Do You Need Wrapper Classes?
  • Declaring an ArrayList or ArrayList
  • Storing primitives as Object references
  • Using Integer.parseInt() or Double.parseDouble()
  • In practice, autoboxing handles the conversion for you — just know it's happening
📦Practice Questions1 link

ArrayList

An ArrayList is a resizable array. Unlike a fixed array, you can add and remove elements at any time. It is the most-tested data structure on FRQ #3.

Creating an ArrayList

import java.util.ArrayList;

// Declare and initialize (empty)
ArrayList<String> names = new ArrayList<String>();
ArrayList<Integer> nums = new ArrayList<Integer>();

All ArrayList Methods

Method What It Does Returns
add(obj) Appends obj to the end boolean (always true)
add(index, obj) Inserts obj at index; shifts others right void
get(index) Returns element at index E (element type)
set(index, obj) Replaces element at index old element
remove(index) Removes and returns element; shifts others left removed element
size() Returns number of elements int

Complete Traced Example

ArrayList<String> list = new ArrayList<String>();
list.add("A");       // [A]              size=1
list.add("B");       // [A, B]           size=2
list.add("C");       // [A, B, C]        size=3
list.add(1, "X");   // [A, X, B, C]     size=4  (B, C shift right)
list.set(0, "Z");    // [Z, X, B, C]     size=4  (A replaced)
list.remove(2);      // [Z, X, C]        size=3  (B removed, C shifts left)
System.out.println(list.get(1));  // X
System.out.println(list.size());   // 3

Traversing an ArrayList

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

// Standard for loop (use when you need the index)
for (int i = 0; i < nums.size(); i++)
    System.out.println(nums.get(i));

// Enhanced for loop (cleaner, use when you only need values)
for (int num : nums)
    System.out.println(num);
ArrayList vs Array — Key Differences
Property Array ArrayList
Size Fixed Dynamic (grows/shrinks)
Element types Primitives or objects Objects only (use wrapper types)
Size syntax arr.length list.size()
Access syntax arr[i] list.get(i)
Modify syntax arr[i] = x list.set(i, x)
Use .equals() to Compare ArrayList Elements
ArrayList<String> list = new ArrayList<String>();
list.add("hello");

// WRONG: == compares references, not content
if (list.get(0) == "hello") ...    // may be false!

// CORRECT: .equals() compares string content
if (list.get(0).equals("hello")) ...  // ✔ always correct

ArrayList Algorithms

ArrayList algorithms are heavily tested on FRQ #3. The most important distinction from array algorithms is how you handle removal — getting this wrong is the most common FRQ mistake.

#1 Mistake: Forward Removal Skips Elements!
ArrayList<Integer> list = new ... // [1, 2, 3, 4]

// WRONG — when element is removed, next element shifts left
// and gets skipped by i++ :
for (int i = 0; i < list.size(); i++)
    if (list.get(i) % 2 == 0) list.remove(i);
// Result: [1, 3]  -- wrong! Should be [1, 3] but arrives there wrong
// Worse: if two consecutive evens [2, 4], the 4 gets skipped entirely

// CORRECT — traverse backward so shifts don't affect unvisited indices
for (int i = list.size() - 1; i >= 0; i--)
    if (list.get(i) % 2 == 0) list.remove(i);

Algorithm 1: Remove All Occurrences of a Value

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

Algorithm 2: Filter into a New List

When you want to keep elements that meet a condition, build a new list rather than removing from the original.

public static ArrayList<Integer> getEvens(ArrayList<Integer> nums)
{
    ArrayList<Integer> result = new ArrayList<Integer>();
    for (int n : nums)
        if (n % 2 == 0) result.add(n);
    return result;
}

Algorithm 3: Find Maximum in an ArrayList

public static int findMax(ArrayList<Integer> nums)
{
    int max = nums.get(0);  // initialize to first element
    for (int n : nums)
        if (n > max) max = n;
    return max;
}

Algorithm 4: Count Elements Meeting a Condition

public static int countAbove(ArrayList<Integer> nums, int threshold)
{
    int count = 0;
    for (int n : nums)
        if (n > threshold) count++;
    return count;
}

Algorithm 5: Sum of All Elements

public static int sumList(ArrayList<Integer> nums)
{
    int sum = 0;
    for (int n : nums) sum += n;
    return sum;
}

Algorithm 6: Find and Update In-Place

// Double every element greater than 10
for (int i = 0; i < list.size(); i++)
    if (list.get(i) > 10)
        list.set(i, list.get(i) * 2);
FRQ Checklist for ArrayList Methods
  • Use list.size() not list.length (no such thing)
  • Use list.get(i) not list[i] (not valid syntax)
  • Use list.set(i, val) to replace, not list[i] = val
  • When removing elements, always traverse backward
  • Use .equals() to compare String elements, never ==

2D Arrays

A 2D array is an array of arrays — think of it as a grid or table with rows and columns. FRQ #4 is always a 2D array problem.

Creating 2D Arrays

// Method 1: Declare size (all elements get default value 0)
int[][] grid = new int[3][4];  // 3 rows, 4 columns

// Method 2: Initialize with values
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
// 3 rows, 3 columns

Accessing Elements and Dimensions

// Access: [row][col] — row first, ALWAYS
System.out.println(matrix[0][0]);  // 1  (row 0, col 0)
System.out.println(matrix[1][2]);  // 6  (row 1, col 2)
System.out.println(matrix[2][1]);  // 8  (row 2, col 1)

// Get dimensions
System.out.println(matrix.length);       // 3 (number of rows)
System.out.println(matrix[0].length);  // 3 (number of columns)

// Modify elements
matrix[1][1] = 99;  // row 1, col 1 changed to 99

2D Array Diagram

//          col 0  col 1  col 2
// row 0:  [  1  ] [  2  ] [  3  ]    matrix[0][0]=1  matrix[0][2]=3
// row 1:  [  4  ] [  5  ] [  6  ]    matrix[1][1]=5  matrix[1][2]=6
// row 2:  [  7  ] [  8  ] [  9  ]    matrix[2][0]=7  matrix[2][2]=9
CRITICAL: [row][col] — Row First, Always!
matrix[1][2]       // Row 1, Col 2 → 6  ✔
matrix[2][1]       // Row 2, Col 1 → 8  (different element!)
matrix.length      // Number of ROWS = 3
matrix[0].length   // Number of COLUMNS = 3

Row-Major Traversal (most common)

Outer loop = rows, inner loop = columns. Visits elements left to right, row by row.

for (int r = 0; r < matrix.length; r++)
{
    for (int c = 0; c < matrix[0].length; c++)
    {
        System.out.print(matrix[r][c] + " ");
    }
    System.out.println();  // newline after each row
}
// Output:
// 1 2 3
// 4 5 6
// 7 8 9

Column-Major Traversal

Outer loop = columns, inner loop = rows. Visits elements top to bottom, column by column.

for (int c = 0; c < matrix[0].length; c++)
{
    for (int r = 0; r < matrix.length; r++)
        System.out.print(matrix[r][c] + " ");
}
// Output: 1 4 7  2 5 8  3 6 9

Enhanced for Loop with 2D Arrays

// "for each row, for each element in that row"
for (int[] row : matrix)
    for (int val : row)
        System.out.print(val + " ");
2D Array Bounds
  • Row indices: 0 to arr.length - 1
  • Column indices: 0 to arr[0].length - 1
  • Always use arr[0].length for columns (not arr.length)
  • For a non-square grid: rows and columns may differ

2D Array Algorithms

2D array algorithms always use nested loops — outer for rows, inner for columns (row-major). FRQ #4 is always a 2D array problem.

Algorithm 1: Sum All Elements

public static int sumAll(int[][] arr)
{
    int sum = 0;
    for (int r = 0; r < arr.length; r++)
        for (int c = 0; c < arr[0].length; c++)
            sum += arr[r][c];
    return sum;
}

Algorithm 2: Count Elements Meeting a Condition

public static int countPositive(int[][] arr)
{
    int count = 0;
    for (int r = 0; r < arr.length; r++)
        for (int c = 0; c < arr[0].length; c++)
            if (arr[r][c] > 0) count++;
    return count;
}

Algorithm 3: Sum a Single Row

public static int rowSum(int[][] arr, int row)
{
    int sum = 0;
    for (int c = 0; c < arr[0].length; c++)
        sum += arr[row][c];
    return sum;
}

Algorithm 4: Sum a Single Column

public static int colSum(int[][] arr, int col)
{
    int sum = 0;
    for (int r = 0; r < arr.length; r++)
        sum += arr[r][col];
    return sum;
}

Algorithm 5: Find Maximum in a 2D Array

public static int findMax(int[][] arr)
{
    int max = arr[0][0];  // initialize to first element
    for (int r = 0; r < arr.length; r++)
        for (int c = 0; c < arr[0].length; c++)
            if (arr[r][c] > max) max = arr[r][c];
    return max;
}

Algorithm 6: Check if Any Row Contains a Value

public static boolean rowContains(int[][] arr, int row, int val)
{
    for (int c = 0; c < arr[0].length; c++)
        if (arr[row][c] == val) return true;
    return false;
}

Algorithm 7: Copy All Elements to a New 2D Array

public static int[][] copy2D(int[][] arr)
{
    int[][] result = new int[arr.length][arr[0].length];
    for (int r = 0; r < arr.length; r++)
        for (int c = 0; c < arr[0].length; c++)
            result[r][c] = arr[r][c];
    return result;
}
FRQ #4 Checklist
  • Outer loop = rows using arr.length
  • Inner loop = columns using arr[0].length
  • Access with arr[r][c]row first, always
  • For a single row: one loop over columns, row index is fixed
  • For a single column: one loop over rows, column index is fixed

4.15 — Sorting Algorithms

The AP exam tests selection sort and insertion sort exclusively. You must trace them pass by pass. You will never be asked to write them from scratch on the MCQ — but you must read them fluently and predict what the array looks like after k passes.

Selection Sort

On each pass, selection sort finds the minimum in the unsorted portion and swaps it into position. After k passes, the first k elements are in their final sorted positions.

public static void selectionSort(int[] data) {
    for (int j = 0; j < data.length - 1; j++) {
        int minIdx = j;
        for (int k = j + 1; k < data.length; k++) {
            if (data[k] < data[minIdx]) {
                minIdx = k;
            }
        }
        int temp = data[j];
        data[j] = data[minIdx];
        data[minIdx] = temp;
    }
}

Pass-by-Pass Trace

Starting array: {42, 17, 35, 8, 26}

Start: { 42, 17, 35, 8, 26 }
Pass 1 (j=0): min=8 at index 3 → swap with index 0 → { 8, 42, 35, 17, 26 }
Pass 2 (j=1): min=17 at index 3 → swap with index 1 → { 8, 17, 35, 42, 26 }
Pass 3 (j=2): min=26 at index 4 → swap with index 2 → { 8, 17, 26, 42, 35 }
Pass 4 (j=3): min=35 at index 4 → swap with index 3 → { 8, 17, 26, 35, 42 }
Selection sort rule: After j passes, the first j elements are fully sorted and will never move again. The outer loop runs data.length - 1 times (not data.length times — the last element falls into place automatically).

Insertion Sort

Insertion sort builds a sorted sub-array on the left. On each pass, it picks the next element and inserts it into the correct position by shifting larger elements rightward.

public static void insertionSort(int[] data) {
    for (int j = 1; j < data.length; j++) {
        int temp = data[j];
        int pos = j - 1;
        while (pos >= 0 && data[pos] > temp) {
            data[pos + 1] = data[pos];
            pos--;
        }
        data[pos + 1] = temp;
    }
}

Pass-by-Pass Trace

Starting array: {42, 17, 35, 8, 26}

Start: { 42, 17, 35, 8, 26 }
Pass 1 (j=1): insert 17 → 42 shifts right → { 17, 42, 35, 8, 26 }
Pass 2 (j=2): insert 35 → 42 shifts right → { 17, 35, 42, 8, 26 }
Pass 3 (j=3): insert 8 → 42, 35, 17 all shift right → { 8, 17, 35, 42, 26 }
Pass 4 (j=4): insert 26 → 42, 35 shift right → { 8, 17, 26, 35, 42 }

Key Differences: Selection vs. Insertion

Property Selection Sort Insertion Sort
Strategy Find min, swap into position Take next element, shift and insert
Outer loop starts at j = 0 j = 1
Swaps per pass Exactly 1 swap 0 to j shifts
After k passes First k elements are final First k+1 elements are sorted (but not necessarily final)
Best case input O(n²) regardless O(n) on already-sorted array
Trace MCQ — Selection Sort State After k Passes
The following method correctly implements selection sort in ascending order.
public static void sSort(int[] d) {
    for (int j = 0; j < d.length - 1; j++) {
        int m = j;
        for (int k = j + 1; k < d.length; k++) {
            if (d[k] < d[m]) m = k;
        }
        int t = d[j]; d[j] = d[m]; d[m] = t;
    }
}
sSort is called with the array {53, 21, 7, 38, 14}. What does the array contain after the outer loop has completed exactly two iterations (after j == 1 finishes)?
A) { 7, 14, 21, 38, 53 }
B) { 7, 14, 53, 38, 21 }
C) { 7, 14, 53, 38, 21 }
D) { 7, 21, 53, 38, 14 }
Answer: C.
Pass 1 (j=0): The minimum of the full array is 7 (index 2). Swap d[0] and d[2]: {7, 21, 53, 38, 14}.
Pass 2 (j=1): The minimum from index 1 onward is 14 (index 4). Swap d[1] and d[4]: {7, 14, 53, 38, 21}.
After two passes, only the first two positions (7 and 14) are in their final sorted places. The rest of the array is in an intermediate state — do not assume it is sorted beyond the first j positions.
Spot the Error MCQ — Insertion Sort
The following method is intended to perform insertion sort in ascending order but does NOT always work correctly.
public static void iSort(int[] d) {
    for (int j = 1; j < d.length; j++) {
        int tmp = d[j];
        int p = j - 1;
        while (p >= 0 && d[p] > tmp) {
            d[p + 1] = d[p];
            p--;
        }
        d[p] = tmp;   // Line 8
    }
}
Which of the following identifies the error?
A) The outer loop should start at j = 0, not j = 1
B) Line 8 should be d[p + 1] = tmp; assigning to d[p] overwrites an element that was not shifted
C) The while condition should use d[p] >= tmp to handle duplicate values
D) tmp should be assigned d[j - 1] rather than d[j]
Answer: B. When the while loop exits, p points to the last element that was not shifted (i.e., the element at d[p] is already ≤ tmp). The correct insertion point is p + 1. Writing d[p] = tmp overwrites a valid sorted element. This is one of the most common off-by-one errors in insertion sort implementations.

4.16 / 4.17 — Recursion & Recursive Searching/Sorting

2025–2026 Exam Scope: You must be able to trace recursive methods and determine their output or return value. You will NOT be asked to write a recursive method from scratch on the MCQ or FRQ. All recursion questions are tracing questions.

Anatomy of a Recursive Method

Every correct recursive method has exactly two components:

1. Base Case

The condition that stops the recursion. Without a base case, you get infinite recursion and a StackOverflowError. The base case returns a value directly — no recursive call is made.

2. Recursive Case

The method calls itself with a smaller or simpler version of the problem. Each recursive call must move closer to the base case.

// Classic example: sum of digits
public static int digitSum(int n) {
    if (n < 10)               // BASE CASE: single digit
        return n;
    return (n % 10) + digitSum(n / 10);  // RECURSIVE CASE
}

How to Trace a Recursive Method

Use the call stack model. Each call is pushed onto the stack. Once the base case is reached, calls start returning and the stack unwinds.

Trace: digitSum(347)

Phase 1 — Building the call stack:

Call Stack (growing downward)
digitSum(347) → 7 + digitSum(34) ...waiting
digitSum(34) → 4 + digitSum(3) ...waiting
digitSum(3) → return 3 BASE CASE ✓

Phase 2 — Unwinding:

Returning (bottom to top)
digitSum(3) = 3
digitSum(34) = 4 + 3 = 7
digitSum(347) = 7 + 7 = 14
Tracing strategy: Write the call chain going down, find the base case, then add up the returns coming back up. Don’t try to hold the whole trace in your head — write it out on scratch paper during the exam.

Common Recursive Patterns on the AP Exam

Pattern What it does Trace clue
mystery(n/10) Processes digits right-to-left Integer division strips the last digit
mystery(n-1) Countdown / factorial-style Counts down to 0 or 1
mystery(n-2) Fibonacci-style Two recursive calls, not one
String slicing: mystery(s.substring(1)) Processes string character by character Each call removes the first character
mystery(arr, index+1) Array traversal via recursion Index walks from 0 to arr.length

Recursive Binary Search (4.17)

/** Precondition: arr is sorted ascending.
 *  Returns index of val, or -1 if not found. */
public static int rBinSearch(int[] arr, int val, int lo, int hi) {
    if (lo > hi)            // BASE CASE: range exhausted
        return -1;
    int mid = (lo + hi) / 2;
    if (arr[mid] == val)    // BASE CASE: found it
        return mid;
    if (arr[mid] < val)
        return rBinSearch(arr, val, mid + 1, hi);  // search right
    return rBinSearch(arr, val, lo, mid - 1);      // search left
}

This is identical in logic to iterative binary search — the only difference is that the loop is replaced with recursive calls. The call stack builds up to O(log n) levels deep.

Trace MCQ — Recursion Return Value
Consider the following method.
public static int proc(int n) {
    if (n <= 0) return 5;
    return n + proc(n - 2);
}
What value is returned by the call proc(6)?
A) 12
B) 17
C) 20
D) 26
Answer: C.
Trace the call chain step by step:
proc(6) = 6 + proc(4)
proc(4) = 4 + proc(2)
proc(2) = 2 + proc(0)
proc(0) = 5 ← base case (n ≤ 0)

Unwinding: proc(2) = 2+5 = 7proc(4) = 4+7 = 11proc(6) = 6+11 = 20

Common mistake: Students often stop at proc(-2) or add an extra call. The base case is n <= 0, which fires at n = 0. The step is n-2, so 6 → 4 → 2 → 0 → base case. That’s 4 active calls, not 3.
I / II / III MCQ — Recursion Behavior
Consider the following method.
public static void printIt(String s) {
    if (s.length() == 0) return;
    System.out.print(s.substring(s.length() - 1));
    printIt(s.substring(0, s.length() - 1));
}
Which of the following statements about a call to printIt("JAVA") are true?

I. The method prints the characters of "JAVA" in reverse order: AVAJ.
II. The base case is reached after exactly 4 recursive calls.
III. The output would be identical if the two statements in the method body were swapped (print first, then recurse, vs. recurse first, then print).
A) I only
B) I and II only
C) II and III only
D) I, II, and III
Answer: A.
Statement I — TRUE: Each call prints the last character of the current string, then recurses on everything except the last character. So the call sequence prints: A, V, A, J → output is AVAJ. Reversed. ✓
Statement II — FALSE: The base case fires when s.length() == 0. Starting with "JAVA" (length 4): "JAV", "JA", "J", "" — that is 4 recursive calls before the base case, but the base case itself is not a recursive call — it is the 5th activation frame. So the base case is reached after 4 recursive calls have been made, but the total number of method activations is 5. This statement is ambiguous enough to be false as written.
Statement III — FALSE: If you swap the statements (recurse first, then print), the recursion builds the full call stack first, and printing happens on the way back up — which would print the characters in forward order (J, A, V, A), the opposite of the current behavior.

⚠ Exam Traps: Search, Sort & Recursion

Binary search on unsorted data: Will not throw an error. May return a wrong index or -1. The AP exam uses this as a trap — code looks correct but the precondition is violated.
Selection sort “after k passes”: Only the first k elements are finalized. Do not assume the rest of the array is in any particular order.
Insertion sort shifts, not swaps: Each pass shifts multiple elements right by one. It does not repeatedly swap adjacent pairs (that’s bubble sort, which is NOT on the AP exam).
Off-by-one in loop bounds: k < arr.length vs k <= arr.length and k <= arr.length - 1 are the most-tested bugs in searching/sorting code.
Recursion returns vs. print: A method that prints output before recursing vs. after recursing produces completely different results. Always trace carefully.
Missing base case vs. wrong base case: A missing base case causes infinite recursion / StackOverflowError. A base case that fires too early returns a wrong value silently — know the difference.
Recursive binary search parameter order: The most common MCQ bug is passing (arr, val, lo, mid-1) instead of (arr, val, mid+1, hi) when the target is in the right half (and vice versa).

Common Exam Traps

1. Off-by-One Loop Bounds

✗ Wrong

i <= arr.length

✔ Correct

i < arr.length

2. length vs length() vs size()

Array arr.length
String str.length()
ArrayList list.size()

3. 2D Array [row][col]

✗ Wrong

grid[col][row]

✔ Correct

grid[row][col]

4. String Comparison

✗ Wrong

str1 == str2

✔ Correct

str1.equals(str2)

5. Enhanced for Can't Modify

for (int num : arr)
    num = num * 2;  // Only changes local copy — array unchanged!

6. Initialize max to arr[0], not 0

int max = 0;       // Fails for all-negative arrays
int max = arr[0];  // ✔ Correct

7. Binary Search Needs Sorted Data

int[] unsorted = {50, 10, 30};
binarySearch(unsorted, 30);  // Unpredictable result!

Practice Questions

Q1: Array Traversal

int[] arr = {2, 4, 6, 8, 10};
int sum = 0;
for (int i = 0; i < arr.length; i += 2)
    sum += arr[i];
System.out.println(sum);
B) 18
Indices 0,2,4: arr[0]+arr[2]+arr[4] = 2+6+10 = 18

Q2: Enhanced For Loop

int[] arr = {1, 2, 3};
for (int num : arr)
    num = num + 10;
System.out.println(arr[1]);
A) 2
Enhanced for loop doesn't modify the array — num is a local copy.

Q3: 2D Array Access

int[][] grid = {{1,2,3}, {4,5,6}, {7,8,9}};
System.out.println(grid[2][0] + grid[0][2]);
B) 10
grid[2][0]=7, grid[0][2]=3, sum=10

Q4: ArrayList Operations

ArrayList<String> list = new ArrayList<String>();
list.add("A"); list.add("B"); list.add("C");
list.add(1, "X");
list.remove(2);
System.out.println(list);
A) [A, X, C]
After adds: [A,B,C]. Insert X at 1: [A,X,B,C]. Remove idx 2 (B): [A,X,C]

FRQ Practice

FRQ 1: ArrayList Processing (FRQ #3 Style)

Part A: Write countAbove returning the count of scores above a threshold.

public static int countAbove(ArrayList<Integer> scores, int threshold)

Part B: Write removeBelow removing all scores below a passing grade.

public static void removeBelow(ArrayList<Integer> scores, int passing)
Part A:
public static int countAbove(ArrayList<Integer> scores, int threshold)
{
    int count = 0;
    for (int score : scores)
        if (score > threshold) count++;
    return count;
}
Part B:
public static void removeBelow(ArrayList<Integer> scores, int passing)
{
    for (int i = scores.size() - 1; i >= 0; i--)
        if (scores.get(i) < passing)
            scores.remove(i);
}
Rubric (Part B — 5 pts)

+1 Traverses backward

+1 Uses get(i) correctly

+1 Compares to passing

+1 Uses remove(i)

+1 No elements skipped

FRQ 2: 2D Array Processing (FRQ #4 Style)

Write countEmpty returning how many null elements are in a 2D String array.

public static int countEmpty(String[][] chart)
public static int countEmpty(String[][] chart)
{
    int count = 0;
    for (int r = 0; r < chart.length; r++)
        for (int c = 0; c < chart[0].length; c++)
            if (chart[r][c] == null) count++;
    return count;
}

FRQ 3: File I/O (NEW Topic!)

Write loadData that reads integers from a file into an ArrayList.

public static ArrayList<Integer> loadData(String filename) throws IOException
public static ArrayList<Integer> loadData(String filename) throws IOException
{
    ArrayList<Integer> data = new ArrayList<Integer>();
    Scanner input = new Scanner(new File(filename));
    while (input.hasNext())
        data.add(input.nextInt());
    input.close();
    return data;
}
AP CSA Exam Tools
Predict your AP score and find out which colleges accept it for credit.

Quick Reference

Structure Create Access Size
Array new int[5] arr[0] arr.length
ArrayList new ArrayList() list.get(0) list.size()
2D Array new int[3][4] grid[r][c] grid.length / grid[0].length

Algorithm Complexity

Algorithm Best Worst Requirement
Linear Search O(1) O(n) None
Binary Search O(1) O(log n) Sorted array!
Selection Sort O(n²) O(n²)
Insertion Sort O(n) O(n²) Best on nearly-sorted

Final Checklist

  • < not <= for loop bounds
  • .length / .length() / .size()
  • grid[row][col] not grid[col][row]
  • Remove from ArrayList BACKWARD
  • .equals() for Strings, not ==
  • Binary search requires a SORTED array
  • Initialize max/min to arr[0], not 0
  • Enhanced for loop cannot modify array elements
  • throws IOException required for file reading
1-on-1 Tutoring
Still unsure after studying? Work directly with Tanner before the exam.
11+ years teaching AP CSA — 54.5% of students score 5s vs. 25.5% nationally.
See Tutoring Options →
AP CSA Exam — May 15, 2026
Where are you right now?
Choose the prep path that matches your situation — not just a product.
Days until exam: days
Best for you now Phase 1 — Study
Just Getting Started
You have time to build a real foundation
Work through the unit study guides in order. Understanding the material deeply now means less panic later — and higher scores on both the MCQ and FRQ.
Start with Study Guides → Free: try a daily practice question Or get a day-by-day plan from the start →
Best for you now Phase 2 — Practice
I Know It — I Need Reps
You understand the concepts but haven't tested yourself yet
The fastest way to find gaps is a full exam under timed conditions. Your weak spots will surface immediately — and you'll know exactly where to focus your remaining time.
Take a Full Practice Exam → Free: browse the FRQ archive (2019–2025) → Or get the 2025 FRQ Year Pack with full solutions →
Best for you now Phase 3 — Focused Review
Running Out of Time
1–3 weeks out and you need a focused plan
Stop browsing random topics. A day-by-day cram plan tells you exactly what to cover each day so nothing important gets skipped and nothing gets over-studied.
Get the Cram Kit → Free: grab the Quick Reference Sheet Or add expert tutoring alongside the plan →
Best for you now Phase 4 — Exam Mode
Exam Is Days Away
You need the fastest path to points right now
days until AP CSA exam
One focused tutoring session with someone who knows exactly what the exam tests is worth more than 10 more hours studying solo. Lock in a slot before they fill up.
Book a Tutor Session → Or get the Cram Kit for a self-study plan
Exam's done — good luck on scores.
AP CSA scores release in July. In the meantime, check out what your score means for college credit and what to expect next.
Browse All AP CSA Resources →

Get in Touch

Whether you're a student, parent, or teacher — I'd love to hear from you.

Just want free AP CS resources?

Enter your email below and check the subscribe box — no message needed. Students get daily practice questions and study tips. Teachers get curriculum resources and teaching strategies.

Typically responds within 24 hours

Message Sent!

Thanks for reaching out. I'll get back to you within 24 hours.

🏫 Welcome, fellow educator!

I offer curriculum resources, practice materials, and study guides designed for AP CS teachers. Let me know what you're looking for — whether it's classroom materials, a guest speaker, or Teachers Pay Teachers resources.

Email

tanner@apcsexamprep.com

📚

Courses

AP CSA, CSP, & Cybersecurity

Response Time

Within 24 hours

Prefer email? Reach me directly at tanner@apcsexamprep.com