algoz
Chapters/01 Java basics/Arrays

Arrays

Store sequences in contiguous memory, access elements by index, and process collections efficiently.

Builds on
You'll gain
Store and walk data in arrayswith fast indexing and safe boundaries
Practice
Sheet coming sooncoding problems for this note
Summary

An array is a fixed-size contiguous sequence of elements of the same type, accessed in a single step through zero-based indexes.

The idea in real life

Think of a row of numbered lockers at a train station. Every locker has the exact same dimensions, sits directly next to its neighbour, and is identified by a clear number starting from 0. If you know locker 0 is at the entrance, you can walk directly to locker 4 without opening or inspecting the lockers in between. Arrays work the same way in Java: they store a fixed number of items of the same type side by side in memory, letting you jump directly to any position using its index.

Why it exists

  1. Storing dozens or hundreds of related values in separate variable names (such as score1, score2, and score3) makes code verbose, repetitive, and impossible to scale dynamically.
  2. Independent variables cannot be traversed systematically inside a loop, requiring manual duplication of code for every calculation.
  3. Grouping elements into a single contiguous structure lets your program index, sort, filter, and pass large datasets across methods with a single reference variable.
  4. Knowing that elements sit side by side in memory allows the computer to calculate memory locations immediately, retrieving any element without scanning prior entries.

How it works

Declaring and creating arrays

Allocate a fixed contiguous sequence of default-initialised elements in memory.

An array is a data structure that stores a fixed number of values of a single type in contiguous memory slots.

To use an array in Java, you perform two distinct steps: declaration and creation.

  • Declaration: You declare an array variable by specifying the element type followed by square brackets [] and the variable identifier, such as int[] scores;. While Java also allows C-style declarations like int scores[];, the convention int[] scores; is preferred because it places the complete type on the left.
  • Creation: You create an array object on the heap using the new keyword, followed by the element type and the desired size in brackets: scores = new int[5];. You can combine declaration and creation on a single line: int[] scores = new int[5];.

When an array is created with new, the JVM automatically initializes every slot to the default value for its type, as taught in variables and primitive types:

  • Numeric primitive types (byte, short, int, long) default to 0 or 0L.
  • Floating-point types (float, double) default to 0.0f or 0.0.
  • Boolean types (boolean) default to false.
  • Character types (char) default to '\u0000' (the null character).
  • Reference types default to null.

If you already know the elements when declaring the array, you can use an array literal enclosed in curly braces { ... }. Java creates the array object on the heap, sizes it to match the number of values provided, and populates the elements in order.

Main.java
public class Main {
    public static void main(String[] args) {
        // Allocate an array of 4 integers with default values.
        int[] scores = new int[4];

        // Allocate and initialise using an array literal.
        int[] primes = { 2, 3, 5, 7 };

        // Default values for primitive types.
        boolean[] flags = new boolean[2];
        double[] measurements = new double[2];

        System.out.println("Default int: " + scores[0]);
        System.out.println("Literal prime: " + primes[2]);
        System.out.println("Default boolean: " + flags[0]);
        System.out.println("Default double: " + measurements[0]);
    }
}
Output
Default int: 0
Literal prime: 5
Default boolean: false
Default double: 0.0

In this program:

  • scores is an array of 4 integers; every slot begins with the default value 0.
  • primes is initialized using an array literal with 4 explicit prime values.
  • flags is an array of 2 booleans, both initialized to false.
  • measurements is an array of 2 doubles, both initialized to 0.0.

Indexing and length

Access elements with zero-based indexes up to length minus one.

Every element in an array is accessed by its position, known as its index.

Java arrays use zero-based indexing:

  • The first element sits at index 0.
  • The second element sits at index 1.
  • The final element sits at index length - 1.

To read a value from an array, write the array variable name followed by the index enclosed in square brackets: scores[0]. To assign a new value to a slot, place the indexed expression on the left side of an assignment operator: scores[0] = 85;.

Every array object has a public final field named length that stores the total number of elements the array can hold. Because length is a field rather than a method, you access it without parentheses: scores.length.

Main.java
public class Main {
    public static void main(String[] args) {
        int[] scores = new int[3];

        scores[0] = 85;
        scores[1] = 92;
        scores[2] = 78;

        int firstScore = scores[0];
        int lastScore = scores[scores.length - 1];

        System.out.println("Array length: " + scores.length);
        System.out.println("First element: " + firstScore);
        System.out.println("Last element: " + lastScore);
    }
}
Output
Array length: 3
First element: 85
Last element: 78

In this program:

  • scores is allocated with length 3.
  • Values 85, 92, and 78 are assigned to indices 0, 1, and 2.
  • scores.length evaluates to 3.
  • scores[scores.length - 1] accesses index 2, which holds the final element 78.

If you attempt to access an index that is less than 0 or greater than or equal to length, Java prevents invalid memory access by immediately throwing an ArrayIndexOutOfBoundsException at runtime:

text
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
	at Main.main(Main.java:4)

The error message explicitly names the invalid index requested (3) and the valid array length (3), helping you pinpoint the off-by-one error in your indexing logic.

Looping over an array

Visit every slot with an indexed loop or clean for-each syntax.

As introduced in loops, repeating operations over sequences is a core programming task. Java provides two primary loop patterns for traversing arrays: indexed for loops and enhanced for-each loops.

An indexed for loop uses a counter i starting at 0 and continuing while i < array.length:

  • It provides direct access to the current index i.
  • It allows you to modify array elements in place (numbers[i] *= 2).
  • It enables traversing backwards, skipping elements, or comparing adjacent pairs (numbers[i] and numbers[i + 1]).

An enhanced for-each loop extracts each element sequentially:

  • It eliminates the need for an index variable and removes boundary conditions, preventing off-by-one errors.
  • It provides read-only access to values; assigning to the loop variable (val = 10;) modifies only that local copy, leaving the original array slot untouched.
Main.java
public class Main {
    public static void main(String[] args) {
        int[] values = { 10, 20, 30, 40 };

        // Indexed loop: access elements and indices.
        for (int i = 0; i < values.length; i++) {
            System.out.println("Index " + i + ": " + values[i]);
        }

        // For-each loop: read each value directly.
        int total = 0;
        for (int val : values) {
            total += val;
        }
        System.out.println("Total sum: " + total);
    }
}
Output
Index 0: 10
Index 1: 20
Index 2: 30
Index 3: 40
Total sum: 100

In this program:

  • The indexed for loop prints each index from 0 to 3 alongside its corresponding value.
  • The enhanced for-each loop visits each element in values, accumulating the numbers into total to produce 100.

Arrays are objects

Array variables hold references that share heap memory on assignment.

In Java, arrays are reference types, not primitive types. An array variable does not store the array elements directly; it stores a reference to an array object on the heap.

When you assign one array variable to another, Java copies the reference, not the underlying array data. Both variables now refer to the same array object. Any modification made through one variable immediately affects what is visible through the other. To make an independent duplicate instead, create a genuine copy (see copying arrays).

Main.java
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        int[] b = a;
        b[0] = 99;
        System.out.println(a[0]);
        System.out.println(b[0]);
    }
}
Output
99
99

Step through the program below to watch both variables point to the same array on the heap. Switch b is to a copy to see how a genuine copy creates a separate array.

As covered in methods, Java passes all method arguments by value. When you pass an array to a method, Java copies the reference into the method parameter. Because both references point to the same heap object, modifying elements inside the method directly updates the caller's array. However, reassigning the parameter variable itself inside the method simply points the local parameter to a different object, leaving the caller's reference unchanged.

Going deeper

Copying arrays

Duplicate elements into independent arrays to prevent unintended shared mutations.

When you want to modify an array without altering the original dataset, you must create a genuine copy rather than assigning references. Java provides four standard ways to duplicate array data:

  1. Arrays.copyOf(original, newLength): Allocates a new array with the specified length and copies elements from the beginning of original. If newLength is greater than original.length, additional slots are initialized to default values. If newLength is smaller, the result is truncated.
  2. Arrays.copyOfRange(original, fromIndex, toIndex): Allocates a new array containing elements from fromIndex (inclusive) to toIndex (exclusive). The length of the new array is toIndex - fromIndex.
  3. System.arraycopy(source, srcPos, destination, destPos, length): A low-level native method that copies length elements from source starting at srcPos into an already allocated destination array starting at destPos. It is implemented directly in native C++ inside the JVM and performs fast bulk memory copies.
  4. original.clone(): Returns a new array containing a shallow copy of all elements in original.
Main.java
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] source = { 10, 20, 30, 40, 50 };

        // Arrays.copyOf creates an independent array of specified length.
        int[] fullCopy = Arrays.copyOf(source, source.length);

        // Arrays.copyOfRange copies a slice [fromIndex, toIndex).
        int[] middleSlice = Arrays.copyOfRange(source, 1, 4);

        // System.arraycopy transfers elements into an existing array.
        int[] target = new int[3];
        System.arraycopy(source, 2, target, 0, 3);

        // Mutating source does not affect genuine copies.
        source[2] = 999;

        System.out.println("source[2]: " + source[2]);
        System.out.println("fullCopy[2]: " + fullCopy[2]);
        System.out.println("middleSlice length: " + middleSlice.length);
        System.out.println("target[0]: " + target[0]);
    }
}
Output
source[2]: 999
fullCopy[2]: 30
middleSlice length: 3
target[0]: 30

In this program:

  • fullCopy is an independent duplicate created by Arrays.copyOf. Modifying source[2] to 999 leaves fullCopy[2] untouched at 30.
  • middleSlice extracts indices 1, 2, and 3 ({ 20, 30, 40 }), producing an array of length 3.
  • System.arraycopy transfers 3 elements starting from source[2] directly into target[0], filling target with { 30, 40, 50 }.

Two-dimensional arrays

Build grids as arrays of arrays with independent row lengths.

In Java, multidimensional arrays are implemented as arrays of arrays. A two-dimensional array is an array object whose individual elements are themselves references to one-dimensional array objects.

To allocate a rectangular grid of 2 rows and 3 columns, write int[][] grid = new int[2][3];:

  • grid.length gives the number of rows (2).
  • grid[r].length gives the number of columns in row r (3).
  • grid[r][c] accesses the element at row index r and column index c.

Because Java represents multidimensional arrays as arrays of array references, rows do not have to be the same size. An array where different rows have different lengths is called a jagged array (or ragged array).

To create a jagged array, declare the outer array with its row count while omitting the inner dimension: int[][] triangle = new int[3][];. You then allocate each individual row array independently with its own length.

Main.java
public class Main {
    public static void main(String[] args) {
        // Standard rectangular matrix: 2 rows, 3 columns.
        int[][] matrix = {
            { 1, 2, 3 },
            { 4, 5, 6 }
        };

        System.out.println("Matrix row count: " + matrix.length);
        System.out.println("Matrix column count: " + matrix[0].length);

        // Jagged array: each row has an independent length.
        int[][] jagged = new int[3][];
        jagged[0] = new int[] { 1 };
        jagged[1] = new int[] { 2, 3 };
        jagged[2] = new int[] { 4, 5, 6 };

        // Row-by-row traversal with nested loops.
        for (int r = 0; r < jagged.length; r++) {
            for (int c = 0; c < jagged[r].length; c++) {
                System.out.print(jagged[r][c] + " ");
            }
            System.out.println();
        }
    }
}
Output
Matrix row count: 2
Matrix column count: 3
1 
2 3 
4 5 6 

In this program:

  • matrix is a rectangular array with 2 rows and 3 columns.
  • jagged is allocated with 3 rows, where row 0 has length 1, row 1 has length 2, and row 2 has length 3.
  • Nested loops iterate through the jagged structure row by row, using jagged[r].length as the inner loop limit.

The Arrays class

Use static helper methods for printing, filling, searching, and sorting.

The standard library class java.util.Arrays provides static helper methods for performing common operations on arrays:

  • Arrays.toString(array): Converts a one-dimensional array into a readable formatted string such as [10, 20, 30]. Passing an array directly to System.out.println prints an object identifier like [I@15db9742; Arrays.toString prints the actual elements.
  • Arrays.deepToString(grid): Recursively converts multidimensional arrays into human-readable strings, formatting all nested levels.
  • Arrays.fill(array, value): Assigns the specified value to every element of the array. You can also specify range boundaries: Arrays.fill(array, fromIndex, toIndex, value).
  • Arrays.sort(array): Sorts elements into ascending numerical order in place. For primitive arrays, Java uses a Dual-Pivot Quicksort algorithm. Sorting algorithms will be studied in depth in chapter 16.
  • Arrays.equals(array1, array2): Returns true if both arrays contain the same number of elements and corresponding pairs of elements are equal. The == operator checks only whether both variables point to the same array object, whereas Arrays.equals compares the contents.
  • Arrays.deepEquals(grid1, grid2): Compares multidimensional arrays for element-by-element equality across all nested dimensions.
  • Arrays.binarySearch(sortedArray, key): Searches a sorted array for a target key, returning its index if found, or a negative insertion point if absent. Searching algorithms are explored in chapter 18.
Main.java
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] numbers = { 50, 20, 40, 10, 30 };

        // Format 1D array as a readable string.
        System.out.println("Before sort: " + Arrays.toString(numbers));

        // Sort primitive array in ascending numerical order in place.
        Arrays.sort(numbers);
        System.out.println("After sort: " + Arrays.toString(numbers));

        // Compare content equality between two arrays.
        int[] expected = { 10, 20, 30, 40, 50 };
        boolean areEqual = Arrays.equals(numbers, expected);
        System.out.println("Equals expected: " + areEqual);

        // Fill an array with a specific value.
        int[] board = new int[4];
        Arrays.fill(board, -1);
        System.out.println("Filled board: " + Arrays.toString(board));

        // Format multidimensional arrays.
        int[][] grid = { { 1, 2 }, { 3, 4 } };
        System.out.println("Grid: " + Arrays.deepToString(grid));
    }
}
Output
Before sort: [50, 20, 40, 10, 30]
After sort: [10, 20, 30, 40, 50]
Equals expected: true
Filled board: [-1, -1, -1, -1]
Grid: [[1, 2], [3, 4]]

In this program:

  • Arrays.toString formats numbers before and after sorting.
  • Arrays.sort rearranges numbers in ascending order in place.
  • Arrays.equals verifies that numbers matches expected element by element.
  • Arrays.fill populates every slot of board with -1.
  • Arrays.deepToString formats the 2D grid array.

Prefix sums

Precompute running totals to calculate range sums with one subtraction.

A frequent challenge in algorithm problems is finding the sum of elements within a slice of an array repeatedly. Summing elements from index left to index right with a loop requires stepping through all elements in that range each time a question is asked. If you answer thousands of queries on a large array, repeating that loop becomes slow.

The prefix sum pattern solves this by precomputing cumulative totals in a single pass:

  1. Allocate an auxiliary array prefix of size nums.length + 1.
  2. Set prefix[0] = 0.
  3. For each index i from 0 to nums.length - 1, compute prefix[i + 1] = prefix[i] + nums[i].

Each slot prefix[k] stores the sum of the first k elements of nums (from index 0 up to k - 1).

Once the prefix array is built, the sum of any subarray from index left to index right (inclusive) is calculated in a single arithmetic operation: rangeSum = prefix[right + 1] - prefix[left];

Because prefix[right + 1] holds the total of elements from index 0 to right, subtracting prefix[left] removes the sum of all elements prior to left, leaving exactly the desired range sum.

When summing many integers, the cumulative total can easily exceed Integer.MAX_VALUE (2,147,483,647), causing integer overflow. To keep calculations exact, declare the prefix array using the 64-bit long type, as covered in variables and primitive types.

Main.java
public class Main {
    public static void main(String[] args) {
        int[] nums = { 3, 1, 4, 1, 5, 9 };

        // Build prefix sum array of size n + 1 with 64-bit long.
        long[] prefix = new long[nums.length + 1];
        for (int i = 0; i < nums.length; i++) {
            prefix[i + 1] = prefix[i] + nums[i];
        }

        // Query sum of range [left, right] inclusive in one subtraction.
        int left = 1;
        int right = 4;
        long rangeSum = prefix[right + 1] - prefix[left];

        System.out.println("Prefix array: ");
        for (int i = 0; i < prefix.length; i++) {
            System.out.print(prefix[i] + " ");
        }
        System.out.println();
        System.out.println("Sum of range [1, 4]: " + rangeSum);
    }
}
Output
Prefix array: 
0 3 4 8 9 14 23 
Sum of range [1, 4]: 11

In this program:

  • nums contains 6 integers.
  • prefix is created with length 7. prefix[0] is 0, and each subsequent slot accumulates nums[i].
  • For left = 1 and right = 4, the range spans elements nums[1] through nums[4] (1 + 4 + 1 + 5 = 11).
  • prefix[5] - prefix[1] calculates 14 - 3 = 11 with a single subtraction.

Under the hood

Memory layout

Elements sit consecutively in memory directly behind the object header.

In Java, every array is an object allocated dynamically on the heap. Its physical structure in memory consists of two distinct regions:

  1. Object Header: A small metadata header maintained by the JVM. It stores internal runtime flags (such as garbage collection state and lock status), a reference to the array's class metadata, and the 32-bit length field. The exact size of this header depends on the JVM architecture and whether compressed reference pointers are enabled.
  2. Element Payload: The contiguous memory block holding the array elements, positioned immediately after the header.

For primitive arrays (such as int[] or double[]), the actual primitive values sit directly inside the payload in consecutive byte addresses. In an int[], each 32-bit integer occupies 4 contiguous bytes: element 0 sits at offset 0 of the payload, element 1 sits at offset 4, element 2 sits at offset 8, and so on.

For reference arrays (such as int[][]), the payload does not store nested data directly. Instead, each slot in the outer array holds an address reference pointing to an independent array object allocated elsewhere on the heap. Because each row is a separate object, row arrays are not guaranteed to sit consecutively in memory.

Why indexing is fast

Find any element's position with one small calculation.

Because primitive array elements sit consecutively in memory and every element of a given type occupies an identical number of bytes, the JVM does not need to walk through elements to locate a specific index.

Instead, the position of element i comes from one calculation: position = start + (i * element_size)

Here:

  • start is where the array's elements begin in memory.
  • element_size is the fixed byte size of the element type (such as 4 bytes for int or 8 bytes for long).
  • i is the requested index.

Modern CPU architectures support this calculation directly in hardware through indexed addressing modes (such as base-plus-scaled-index instructions). As a result, reading scores[0] and reading scores[500000] execute in the exact same number of machine cycles. Access time does not depend on the total length of the array or the magnitude of the index. In chapter 06, you will learn how Big-O notation formalises this property as constant-time performance.

Bounds checks

The JVM verifies bounds in bytecode to ensure memory safety.

In low-level languages like C, accessing an out-of-bounds index reads or overwrites whatever data happens to sit at that calculated position, creating severe memory corruption and security vulnerabilities. Java eliminates this risk by mandating bounds checks at the virtual machine level.

Every array access in compiled Java bytecode is performed by dedicated instructions:

  • iaload reads an integer from an array reference and index on the operand stack.
  • iastore writes an integer into an array slot from the operand stack.
  • arraylength extracts the length from the array object header.

Under §6.5 of the Java Virtual Machine Specification, whenever iaload or iastore executes, the JVM automatically checks two conditions:

  1. The array reference must not be null (otherwise it throws NullPointerException).
  2. The index must satisfy 0 <= index < arraylength (otherwise it throws ArrayIndexOutOfBoundsException).

Inspecting bytecode with javap -c reveals these exact instructions:

text
Compiled from "ArrayOperations.java"
public class ArrayOperations {
  static int getElement(int[], int);
    Code:
       0: aload_0
       1: iload_1
       2: iaload
       3: ireturn

  static void setElement(int[], int, int);
    Code:
       0: aload_0
       1: iload_1
       2: iload_2
       3: iastore
       4: return

  static int getLength(int[]);
    Code:
       0: aload_0
       1: arraylength
       2: ireturn
}

In this compiled bytecode:

  • In getElement, aload_0 pushes the array reference and iload_1 pushes the index onto the operand stack. iaload verifies the bounds and pushes the retrieved integer.
  • In setElement, aload_0, iload_1, and iload_2 push the array reference, index, and value. iastore verifies the bounds and stores the value.
  • In getLength, arraylength reads the length field directly from the array header.

Although the specification requires bounds checks on every access, modern Just-In-Time (JIT) compilers optimize hot loops. When the JIT compiler analyzes a loop header like for (int i = 0; i < nums.length; i++), it can mathematically prove that i never falls below 0 and never reaches nums.length. In such cases, the JIT compiler safely hoists or eliminates the individual boundary checks from the compiled machine code, providing full memory safety with maximum execution speed.

Fixed size

Array capacity is permanent once allocated on the heap.

When an array is allocated with new, the JVM reserves a contiguous chunk of memory on the heap sized precisely for the requested number of elements. Once this block is allocated, its size cannot grow or shrink.

An array's size is fixed because contiguous memory directly adjacent to the array may already be occupied by other objects on the heap. Expanding the array in place would overwrite adjacent heap data.

If your application needs to store more elements than the allocated array can hold, you must allocate a new, larger array and copy the existing elements over using Arrays.copyOf or System.arraycopy. In chapter 04, you will learn how the ArrayList class automates this resizing process.

In Java

Here is a complete program that brings these concepts together: inspecting bounds, accumulating prefix sums with long arithmetic, copying arrays safely, sorting and searching elements, and traversing a multidimensional grid.

In this program:

  • findMin locates the smallest integer in an array.
  • buildPrefixSums constructs a cumulative prefix sum array using 64-bit long storage.
  • queryRangeSum calculates the sum of elements between left and right inclusive using a single subtraction.
  • sumGrid computes the total sum of all values in a two-dimensional grid.
Main.java
import java.util.Arrays;

public class Main {
    // Returns the smallest integer found in the provided array.
    static int findMin(int[] numbers) {
        int lowest = numbers[0];
        for (int i = 1; i < numbers.length; i++) {
            if (numbers[i] < lowest) {
                lowest = numbers[i];
            }
        }
        return lowest;
    }

    // Returns a prefix sum array computed with 64-bit long accumulation.
    static long[] buildPrefixSums(int[] numbers) {
        long[] prefix = new long[numbers.length + 1];
        for (int i = 0; i < numbers.length; i++) {
            prefix[i + 1] = prefix[i] + numbers[i];
        }
        return prefix;
    }

    // Returns the sum of elements between left and right inclusive.
    static long queryRangeSum(long[] prefix, int left, int right) {
        return prefix[right + 1] - prefix[left];
    }

    // Calculates and returns the sum of all elements in a 2D grid.
    static long sumGrid(int[][] grid) {
        long total = 0;
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[r].length; c++) {
                total += grid[r][c];
            }
        }
        return total;
    }

    public static void main(String[] args) {
        int[] scores = { 45, 12, 89, 33, 71 };

        // Inspect boundaries and values.
        int minScore = findMin(scores);
        System.out.println("Original scores: " + Arrays.toString(scores));
        System.out.println("Minimum score: " + minScore);

        // Calculate prefix sums and answer range queries.
        long[] prefix = buildPrefixSums(scores);
        long rangeTotal = queryRangeSum(prefix, 1, 3);
        System.out.println("Sum of range [1, 3]: " + rangeTotal);

        // Create an independent copy and sort it.
        int[] sortedScores = Arrays.copyOf(scores, scores.length);
        Arrays.sort(sortedScores);
        System.out.println("Sorted copy: " + Arrays.toString(sortedScores));
        System.out.println("Original unchanged: " + Arrays.toString(scores));

        // Binary search on sorted array.
        int targetIndex = Arrays.binarySearch(sortedScores, 45);
        System.out.println("Index of 45 in sorted: " + targetIndex);

        // Process a 2D grid.
        int[][] grid = {
            { 10, 20 },
            { 30, 40, 50 }
        };
        System.out.println("Grid: " + Arrays.deepToString(grid));
        System.out.println("Grid total sum: " + sumGrid(grid));
    }
}
Output
Original scores: [45, 12, 89, 33, 71]
Minimum score: 12
Sum of range [1, 3]: 134
Sorted copy: [12, 33, 45, 71, 89]
Original unchanged: [45, 12, 89, 33, 71]
Index of 45 in sorted: 2
Grid: [[10, 20], [30, 40, 50]]
Grid total sum: 150

Traps

  1. Using length instead of length - 1 as the last index

    Because Java arrays use zero-based indexing, valid element positions range from 0 to nums.length - 1. Attempting to read or write at nums.length exceeds the allocated boundary and causes the JVM to throw ArrayIndexOutOfBoundsException at runtime.

    ✕ WRONGint last = nums[nums.length];
    ✓ RIGHTint last = nums[nums.length - 1];
  2. Comparing array contents with == instead of Arrays.equals

    The == operator on arrays checks whether both variables point to the same array object. Even if two separate arrays have identical lengths and element values, firstArray == secondArray evaluates to false. Use Arrays.equals to verify that corresponding elements match.

    ✕ WRONGif (firstArray == secondArray) { ... }
    ✓ RIGHTif (Arrays.equals(firstArray, secondArray)) { ... }
  3. Printing an array directly with println

    Passing an array reference directly to System.out.println invokes Object.toString(), which prints the internal type descriptor and hexadecimal identity hash code (such as [I@15db9742) rather than the array contents. Use Arrays.toString(scores) for one-dimensional arrays or Arrays.deepToString(grid) for nested arrays.

    ✕ WRONGSystem.out.println(scores);
    ✓ RIGHTSystem.out.println(Arrays.toString(scores));
  4. Assigning an array variable instead of copying elements

    Assigning one array variable to another copies only the reference value, not the array data on the heap. Both variables point to the exact same memory slots, meaning modifications made through backup immediately change original. Use Arrays.copyOf or clone() when you need an independent duplicate.

    ✕ WRONGint[] backup = original;
    ✓ RIGHTint[] backup = Arrays.copyOf(original, original.length);
  5. Accessing unallocated rows in a jagged array

    When creating a multidimensional array with unspecified inner dimensions, Java initializes each row reference to null. Attempting to read or assign an element in an unallocated row attempts to dereference a null reference, throwing a NullPointerException. Allocate each inner row array before accessing its elements.

    ✕ WRONGint[][] grid = new int[3][]; grid[0][0] = 5;
    ✓ RIGHTint[][] grid = new int[3][]; grid[0] = new int[2]; grid[0][0] = 5;
  6. Sharing a single row array across multiple rows with Arrays.fill

    Passing a single row array reference to Arrays.fill stores the exact same reference into every slot of grid. Modifying grid[0][0] mutates grid[1][0] and grid[2][0] because all rows share the same underlying array object. Instantiate a new array for each row inside a loop instead.

    ✕ WRONGint[] row = new int[3]; Arrays.fill(grid, row);
    ✓ RIGHTfor (int i = 0; i < grid.length; i++) { grid[i] = new int[3]; }
  7. Allowing prefix sums to overflow a 32-bit integer

    Accumulating multiple values inside a prefix sum array can easily exceed Integer.MAX_VALUE (2,147,483,647), causing silent two's complement integer overflow as taught in variables and primitive types. Declare the prefix accumulator array with type long[] to prevent incorrect negative sums.

    ✕ WRONGint[] prefix = new int[nums.length + 1];
    ✓ RIGHTlong[] prefix = new long[nums.length + 1];
  8. Calling length as a method on arrays

    The size of an array is exposed as a public final field named length, not a method. Appending parentheses () is invalid syntax on arrays and causes a compile-time error: cannot find symbol method length(). Strings use length(), while arrays use length.

    ✕ WRONGfor (int i = 0; i < nums.length(); i++)
    ✓ RIGHTfor (int i = 0; i < nums.length; i++)
AlgozSet in Geist & Geist MonoJava track