Java Arrays

1. What is an Array?

An array in Java is a fixed-size container that holds a collection of elements of the same data type, stored in contiguous memory locations. Once you create an array with a specific size, that size cannot change for the lifetime of the array.

Think of an array like a row of mailboxes in an apartment building. Each mailbox is numbered starting from 0, every mailbox is the same size (same data type), the total number of mailboxes is fixed when the building is constructed, and you can go directly to any mailbox if you know its number.

Key characteristics of Java arrays:

  • Fixed size — The length is set at creation time and cannot grow or shrink. If you need a resizable collection, use ArrayList.
  • Zero-indexed — The first element is at index 0, the second at index 1, and the last at index length - 1.
  • Homogeneous — All elements must be the same type (or a subtype, for object arrays).
  • Contiguous memory — Elements are stored next to each other in memory, which makes access by index extremely fast (O(1) constant time).
  • Object type — In Java, every array is an object. It is allocated on the heap, and the variable holds a reference to the array object.
  • Can hold primitives or objectsint[], double[], String[], Employee[] are all valid array types.
// Visualizing an array in memory
// Index:    [0]    [1]    [2]    [3]    [4]
// Value:     10     20     30     40     50
// Address: 0x100  0x104  0x108  0x10C  0x110  (contiguous 4-byte int blocks)

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

System.out.println(scores[0]);  // 10  -- first element
System.out.println(scores[4]);  // 50  -- last element
System.out.println(scores.length);  // 5  -- total number of elements

2. Declaring and Creating Arrays

There are three distinct steps when working with arrays: declaration (telling the compiler what type of array you want), instantiation (allocating memory for the array), and initialization (filling the array with values). These steps can be done separately or combined into a single statement.

Declaration Syntax

Java supports two styles for declaring an array variable. Both are equivalent, but the first style is preferred because it keeps the type information together.

// Preferred style: brackets after the type
int[] numbers;
String[] names;
double[] prices;

// Also valid but less common: brackets after the variable name (C-style)
int scores[];
String cities[];

// The preferred style makes it clearer that the TYPE is "int array"
// rather than the variable being an "array version" of int

Instantiation with new

The new keyword allocates memory for the array on the heap. You must specify the size, and it cannot be changed later.

// Declare and instantiate separately
int[] numbers;
numbers = new int[5];  // creates an array of 5 ints, all initialized to 0

// Declare and instantiate in one line
String[] names = new String[3];  // creates an array of 3 Strings, all initialized to null
double[] prices = new double[10];  // creates an array of 10 doubles, all initialized to 0.0

// The size can be a variable or expression
int studentCount = 30;
int[] grades = new int[studentCount];
boolean[] flags = new boolean[studentCount * 2];

Array Literal Initialization

When you know the values at compile time, you can use an array literal (also called an array initializer). The compiler infers the size from the number of values you provide.

// Array literal -- size is inferred (5 elements)
int[] primes = {2, 3, 5, 7, 11};

// Array literal with Strings
String[] weekdays = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};

// Array literal with doubles
double[] temperatures = {98.6, 99.1, 97.8, 100.4, 98.2};

// You can also use new with a literal (required when not in a declaration)
int[] evenNumbers = new int[]{2, 4, 6, 8, 10};

// This form is necessary when passing an array inline to a method
printArray(new int[]{1, 2, 3});  // cannot write printArray({1, 2, 3})

Default Values

When you create an array with new but do not provide initial values, Java fills every element with the default value for that type.

Array Type Default Value Example
int[], short[], byte[], long[] 0 new int[3]{0, 0, 0}
float[], double[] 0.0 new double[2]{0.0, 0.0}
boolean[] false new boolean[2]{false, false}
char[] '\u0000' (null character) new char[2]{'\u0000', '\u0000'}
String[], any object array null new String[2]{null, null}
import java.util.Arrays;

int[] numbers = new int[5];
System.out.println(Arrays.toString(numbers));
// Output: [0, 0, 0, 0, 0]

boolean[] flags = new boolean[3];
System.out.println(Arrays.toString(flags));
// Output: [false, false, false]

String[] names = new String[4];
System.out.println(Arrays.toString(names));
// Output: [null, null, null, null]

double[] prices = new double[3];
System.out.println(Arrays.toString(prices));
// Output: [0.0, 0.0, 0.0]

3. Accessing and Modifying Elements

Array elements are accessed and modified using the index operator []. The index must be an integer between 0 (inclusive) and array.length - 1 (inclusive). Accessing an index outside this range throws an ArrayIndexOutOfBoundsException at runtime.

String[] colors = new String[4];

// Setting values (writing)
colors[0] = "Red";
colors[1] = "Green";
colors[2] = "Blue";
colors[3] = "Yellow";

// Getting values (reading)
System.out.println(colors[0]);  // Red
System.out.println(colors[2]);  // Blue

// Modifying an existing value
colors[1] = "Purple";
System.out.println(colors[1]);  // Purple

// The length property (not a method -- no parentheses)
System.out.println("Array size: " + colors.length);  // Array size: 4

// Accessing the last element
System.out.println("Last: " + colors[colors.length - 1]);  // Last: Yellow

ArrayIndexOutOfBoundsException

This is one of the most common runtime errors in Java. It occurs when you try to access an index that does not exist in the array. The compiler cannot catch this — it only happens at runtime.

int[] numbers = {10, 20, 30};

// Valid indices: 0, 1, 2
System.out.println(numbers[0]);  // 10
System.out.println(numbers[2]);  // 30

// RUNTIME ERROR: index 3 does not exist (valid range is 0-2)
// System.out.println(numbers[3]);
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3

// RUNTIME ERROR: negative indices are never valid
// System.out.println(numbers[-1]);
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 3

// Common bug: using <= instead of < in a loop
// for (int i = 0; i <= numbers.length; i++) {  // BUG: runs one iteration too many
//     System.out.println(numbers[i]);
// }

// CORRECT
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}
// Output:
// 10
// 20
// 30

4. Iterating Over Arrays

There are several ways to iterate through an array in Java. Each approach has its strengths, and knowing when to use which one is important for writing clean, efficient code.

Traditional for Loop

The traditional for loop gives you full control: you know the index, you can iterate forwards or backwards, you can skip elements, and you can modify elements in place. Use this when you need the index or when you need to change element values.

String[] fruits = {"Apple", "Banana", "Cherry", "Date", "Elderberry"};

// Forward iteration
for (int i = 0; i < fruits.length; i++) {
    System.out.println("Index " + i + ": " + fruits[i]);
}
// Output:
// Index 0: Apple
// Index 1: Banana
// Index 2: Cherry
// Index 3: Date
// Index 4: Elderberry

// Reverse iteration
for (int i = fruits.length - 1; i >= 0; i--) {
    System.out.println(fruits[i]);
}
// Output:
// Elderberry
// Date
// Cherry
// Banana
// Apple

// Modify elements in place (convert to uppercase)
for (int i = 0; i < fruits.length; i++) {
    fruits[i] = fruits[i].toUpperCase();
}
System.out.println(java.util.Arrays.toString(fruits));
// Output: [APPLE, BANANA, CHERRY, DATE, ELDERBERRY]

Enhanced for-each Loop

The for-each loop (introduced in Java 5) is the cleanest way to iterate when you just need to read each element and do not need the index. It eliminates off-by-one errors entirely.

int[] scores = {95, 87, 76, 92, 88};

// Read each element -- no index variable needed
for (int score : scores) {
    System.out.println("Score: " + score);
}
// Output:
// Score: 95
// Score: 87
// Score: 76
// Score: 92
// Score: 88

// Calculate average
int sum = 0;
for (int score : scores) {
    sum += score;
}
double average = (double) sum / scores.length;
System.out.println("Average: " + average);
// Output: Average: 87.6

// WARNING: Assigning to the loop variable does NOT modify the array
for (int score : scores) {
    score = score + 10;  // this changes the local copy only
}
System.out.println(java.util.Arrays.toString(scores));
// Output: [95, 87, 76, 92, 88]  -- unchanged!

while Loop

A while loop is rarely the first choice for iterating arrays, but it is useful when you want to stop iteration early based on a dynamic condition or when you are processing elements until you find something specific.

String[] tasks = {"Email client", "Fix login bug", "URGENT: Deploy hotfix", "Write tests", "Code review"};

// Find and process the first urgent task
int index = 0;
while (index < tasks.length) {
    if (tasks[index].startsWith("URGENT")) {
        System.out.println("Found urgent task at index " + index + ": " + tasks[index]);
        break;
    }
    index++;
}
// Output: Found urgent task at index 2: URGENT: Deploy hotfix

Arrays.stream() (Java 8+)

The Stream API provides a functional, declarative approach to array processing. It is especially powerful when you need to chain operations like filtering, mapping, and reducing.

import java.util.Arrays;
import java.util.stream.IntStream;

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// Print each element
Arrays.stream(numbers).forEach(n -> System.out.print(n + " "));
// Output: 1 2 3 4 5 6 7 8 9 10

System.out.println();

// Filter and transform: get squares of even numbers
int[] evenSquares = Arrays.stream(numbers)
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .toArray();
System.out.println(Arrays.toString(evenSquares));
// Output: [4, 16, 36, 64, 100]

// Sum all elements
int sum = Arrays.stream(numbers).sum();
System.out.println("Sum: " + sum);
// Output: Sum: 55

// Find max
int max = Arrays.stream(numbers).max().orElse(0);
System.out.println("Max: " + max);
// Output: Max: 10

// String array with streams
String[] languages = {"Java", "Python", "JavaScript", "Go", "Rust"};
Arrays.stream(languages)
    .filter(lang -> lang.length() > 3)
    .map(String::toUpperCase)
    .forEach(System.out::println);
// Output:
// JAVA
// PYTHON
// JAVASCRIPT
// RUST

When to Use Each Approach

Approach Best For Gives Index? Can Modify?
for (int i = 0; ...) Need index, modify in place, iterate backwards Yes Yes
for (Type x : arr) Read-only traversal of all elements No No (local copy)
while Conditional early termination Yes (manual) Yes
Arrays.stream() Functional pipelines: filter, map, reduce No (unless IntStream.range) No (produces new array)

5. Multi-Dimensional Arrays

A multi-dimensional array is an array of arrays. The most common form is the 2D array, which you can think of as a table with rows and columns. Java also supports 3D arrays and beyond, though anything past 2D is rare in practice.

2D Arrays (Matrix)

A 2D array is declared with two sets of brackets. The first dimension represents rows and the second represents columns.

// Declare and instantiate a 3x4 matrix (3 rows, 4 columns)
int[][] matrix = new int[3][4];

// Set values
matrix[0][0] = 1;
matrix[0][1] = 2;
matrix[1][2] = 5;
matrix[2][3] = 9;

// Initialize with literal values
int[][] grid = {
    {1, 2, 3, 4},     // row 0
    {5, 6, 7, 8},     // row 1
    {9, 10, 11, 12}   // row 2
};

// Access elements
System.out.println(grid[0][0]);  // 1  (row 0, col 0)
System.out.println(grid[1][2]);  // 7  (row 1, col 2)
System.out.println(grid[2][3]);  // 12 (row 2, col 3)

// Dimensions
System.out.println("Rows: " + grid.length);        // 3
System.out.println("Columns: " + grid[0].length);   // 4

Iterating Over a 2D Array

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// Traditional nested for loop -- gives you row and column indices
System.out.println("Using traditional for loop:");
for (int row = 0; row < matrix.length; row++) {
    for (int col = 0; col < matrix[row].length; col++) {
        System.out.printf("%4d", matrix[row][col]);
    }
    System.out.println();
}
// Output:
//    1   2   3
//    4   5   6
//    7   8   9

// Enhanced for-each loop -- cleaner when you don't need indices
System.out.println("\nUsing for-each loop:");
for (int[] row : matrix) {
    for (int value : row) {
        System.out.printf("%4d", value);
    }
    System.out.println();
}
// Output:
//    1   2   3
//    4   5   6
//    7   8   9

// Sum all elements
int total = 0;
for (int[] row : matrix) {
    for (int value : row) {
        total += value;
    }
}
System.out.println("Sum of all elements: " + total);
// Output: Sum of all elements: 45

Jagged Arrays (Rows with Different Lengths)

In Java, a 2D array is actually an array of arrays, and each inner array can have a different length. This is called a jagged array (or ragged array). This is different from languages like C where a 2D array must be rectangular.

// Jagged array: each row has a different number of columns
int[][] jagged = new int[3][];        // 3 rows, columns not yet defined
jagged[0] = new int[]{1, 2};         // row 0 has 2 columns
jagged[1] = new int[]{3, 4, 5, 6};   // row 1 has 4 columns
jagged[2] = new int[]{7, 8, 9};      // row 2 has 3 columns

// Iterate safely using each row's own length
for (int i = 0; i < jagged.length; i++) {
    System.out.print("Row " + i + " (" + jagged[i].length + " elements): ");
    for (int j = 0; j < jagged[i].length; j++) {
        System.out.print(jagged[i][j] + " ");
    }
    System.out.println();
}
// Output:
// Row 0 (2 elements): 1 2
// Row 1 (4 elements): 3 4 5 6
// Row 2 (3 elements): 7 8 9

// Practical example: storing student grades where each student took a different number of exams
int[][] studentGrades = {
    {90, 85, 92},           // Student 0: took 3 exams
    {78, 88, 95, 82},       // Student 1: took 4 exams
    {91, 76},               // Student 2: took 2 exams
    {88, 93, 79, 84, 90}    // Student 3: took 5 exams
};

for (int s = 0; s < studentGrades.length; s++) {
    int sum = 0;
    for (int grade : studentGrades[s]) {
        sum += grade;
    }
    double avg = (double) sum / studentGrades[s].length;
    System.out.printf("Student %d: %d exams, average = %.1f%n", s, studentGrades[s].length, avg);
}
// Output:
// Student 0: 3 exams, average = 89.0
// Student 1: 4 exams, average = 85.8
// Student 2: 2 exams, average = 83.5
// Student 3: 5 exams, average = 86.8

3D Arrays (Brief)

A 3D array adds another dimension. You can think of it as a collection of 2D tables (like pages in a book, each page being a table). Three-dimensional arrays are uncommon outside of scientific computing and image processing.

// 3D array: 2 "layers", each with 3 rows and 4 columns
int[][][] cube = new int[2][3][4];

cube[0][0][0] = 1;
cube[1][2][3] = 99;

// Initialize with literal
int[][][] data = {
    {   // layer 0
        {1, 2},
        {3, 4}
    },
    {   // layer 1
        {5, 6},
        {7, 8}
    }
};

// Print all elements
for (int layer = 0; layer < data.length; layer++) {
    System.out.println("Layer " + layer + ":");
    for (int row = 0; row < data[layer].length; row++) {
        for (int col = 0; col < data[layer][row].length; col++) {
            System.out.print(data[layer][row][col] + " ");
        }
        System.out.println();
    }
    System.out.println();
}
// Output:
// Layer 0:
// 1 2
// 3 4
//
// Layer 1:
// 5 6
// 7 8

6. The Arrays Utility Class

The java.util.Arrays class provides a rich set of static methods for working with arrays. Before writing your own array logic, check if Arrays already has a method for it -- it almost certainly does, and the built-in implementations are optimized and well-tested.

Arrays.toString() and Arrays.deepToString()

Printing an array directly with System.out.println() produces a useless memory reference like [I@1b6d3586. Use Arrays.toString() for 1D arrays and Arrays.deepToString() for multi-dimensional arrays.

import java.util.Arrays;

int[] numbers = {5, 3, 8, 1, 9};

// Without Arrays.toString() -- NOT useful
System.out.println(numbers);
// Output: [I@1b6d3586  (memory reference, not the contents)

// With Arrays.toString() -- human-readable
System.out.println(Arrays.toString(numbers));
// Output: [5, 3, 8, 1, 9]

// For 2D arrays, use deepToString()
int[][] matrix = {{1, 2, 3}, {4, 5, 6}};

System.out.println(Arrays.toString(matrix));
// Output: [[I@4554617c, [I@74a14482]  -- toString only shows inner array references

System.out.println(Arrays.deepToString(matrix));
// Output: [[1, 2, 3], [4, 5, 6]]  -- deepToString shows actual values

Arrays.sort()

Sorts the array in ascending order. For primitives, it uses a dual-pivot Quicksort (O(n log n) average). For objects, it uses TimSort (stable, O(n log n)). You can sort the entire array or a subrange.

import java.util.Arrays;
import java.util.Collections;

// Sort integers in ascending order
int[] numbers = {5, 2, 8, 1, 9, 3};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers));
// Output: [1, 2, 3, 5, 8, 9]

// Sort a subrange: indices 1 (inclusive) to 4 (exclusive)
int[] partial = {50, 30, 10, 40, 20};
Arrays.sort(partial, 1, 4);  // sorts indices 1, 2, 3 only
System.out.println(Arrays.toString(partial));
// Output: [50, 10, 30, 40, 20]

// Sort Strings alphabetically
String[] fruits = {"Banana", "Apple", "Cherry", "Date"};
Arrays.sort(fruits);
System.out.println(Arrays.toString(fruits));
// Output: [Apple, Banana, Cherry, Date]

// Sort Strings in descending order (requires wrapper array for Collections.reverseOrder)
String[] cities = {"New York", "London", "Tokyo", "Sydney"};
Arrays.sort(cities, Collections.reverseOrder());
System.out.println(Arrays.toString(cities));
// Output: [Tokyo, Sydney, New York, London]

// Sort Integer wrapper array in descending order
Integer[] values = {5, 2, 8, 1, 9, 3};
Arrays.sort(values, Collections.reverseOrder());
System.out.println(Arrays.toString(values));
// Output: [9, 8, 5, 3, 2, 1]

Arrays.binarySearch()

Performs a binary search on a sorted array and returns the index of the target element. If the element is not found, it returns -(insertion point) - 1. The array must be sorted first or the result is undefined.

import java.util.Arrays;

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

// Search for existing element
int index = Arrays.binarySearch(sorted, 40);
System.out.println("40 found at index: " + index);
// Output: 40 found at index: 3

// Search for non-existing element
int notFound = Arrays.binarySearch(sorted, 35);
System.out.println("35 result: " + notFound);
// Output: 35 result: -4
// The value -4 means: not found, and the insertion point would be index 3
// Formula: -(insertion point) - 1 = -(3) - 1 = -4

// Practical use: check if element exists
String[] names = {"Alice", "Bob", "Charlie", "Diana"};
Arrays.sort(names);  // MUST sort before binary search

String target = "Charlie";
int result = Arrays.binarySearch(names, target);
if (result >= 0) {
    System.out.println(target + " found at index " + result);
} else {
    System.out.println(target + " not found");
}
// Output: Charlie found at index 2

Arrays.fill()

Sets every element in an array (or a subrange) to the same value. Useful for initializing arrays with a non-default value.

import java.util.Arrays;

// Fill entire array
int[] scores = new int[5];
Arrays.fill(scores, -1);
System.out.println(Arrays.toString(scores));
// Output: [-1, -1, -1, -1, -1]

// Fill a subrange: indices 1 (inclusive) to 4 (exclusive)
boolean[] flags = new boolean[6];
Arrays.fill(flags, 1, 4, true);
System.out.println(Arrays.toString(flags));
// Output: [false, true, true, true, false, false]

// Practical: initialize a grid with dots
char[][] board = new char[3][3];
for (char[] row : board) {
    Arrays.fill(row, '.');
}
System.out.println(Arrays.deepToString(board));
// Output: [[., ., .], [., ., .], [., ., .]]

Arrays.copyOf() and Arrays.copyOfRange()

Create new arrays by copying all or part of an existing array. These methods handle the new allocation for you.

import java.util.Arrays;

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

// Copy entire array
int[] copy = Arrays.copyOf(original, original.length);
System.out.println(Arrays.toString(copy));
// Output: [10, 20, 30, 40, 50]

// Copy with a larger size (pads with default value)
int[] expanded = Arrays.copyOf(original, 8);
System.out.println(Arrays.toString(expanded));
// Output: [10, 20, 30, 40, 50, 0, 0, 0]

// Copy with a smaller size (truncates)
int[] truncated = Arrays.copyOf(original, 3);
System.out.println(Arrays.toString(truncated));
// Output: [10, 20, 30]

// Copy a subrange: from index 1 (inclusive) to 4 (exclusive)
int[] sub = Arrays.copyOfRange(original, 1, 4);
System.out.println(Arrays.toString(sub));
// Output: [20, 30, 40]

Arrays.equals() and Arrays.deepEquals()

Compare array contents for equality. The == operator only checks if two variables point to the same array object in memory -- it does not compare contents.

import java.util.Arrays;

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

// == compares references (memory addresses), NOT contents
System.out.println(a == b);   // false -- different objects in memory
System.out.println(a == c);   // true  -- same object (c points to a)

// Arrays.equals() compares contents element by element
System.out.println(Arrays.equals(a, b));  // true -- same values
System.out.println(Arrays.equals(a, c));  // true -- same values (and same object)

// For 2D arrays, use deepEquals()
int[][] matrix1 = {{1, 2}, {3, 4}};
int[][] matrix2 = {{1, 2}, {3, 4}};

System.out.println(Arrays.equals(matrix1, matrix2));      // false -- compares inner array references
System.out.println(Arrays.deepEquals(matrix1, matrix2));   // true  -- compares actual nested values

Arrays.asList()

Converts an array to a List. The resulting list is a fixed-size wrapper backed by the original array -- you can modify elements, but you cannot add or remove elements.

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

String[] colors = {"Red", "Green", "Blue"};

// Fixed-size list backed by the array
List colorList = Arrays.asList(colors);
System.out.println(colorList);
// Output: [Red, Green, Blue]

// You CAN modify existing elements
colorList.set(0, "Purple");
System.out.println(colorList);
// Output: [Purple, Green, Blue]

// Changes are reflected in the original array (they share the same storage)
System.out.println(colors[0]);
// Output: Purple

// You CANNOT add or remove elements
// colorList.add("Yellow");     // throws UnsupportedOperationException
// colorList.remove(0);         // throws UnsupportedOperationException

// To get a fully mutable list, wrap it in a new ArrayList
List mutableList = new ArrayList<>(Arrays.asList(colors));
mutableList.add("Yellow");
mutableList.remove("Green");
System.out.println(mutableList);
// Output: [Purple, Blue, Yellow]

Quick Reference: Arrays Utility Methods

Method Purpose Requires Sorted?
Arrays.toString(arr) Readable string representation of 1D array No
Arrays.deepToString(arr) Readable string representation of multi-dimensional array No
Arrays.sort(arr) Sort in ascending order No (it does the sorting)
Arrays.binarySearch(arr, key) Find index of element (O(log n)) Yes
Arrays.fill(arr, val) Set all elements to the same value No
Arrays.copyOf(arr, len) Copy array with new length No
Arrays.copyOfRange(arr, from, to) Copy a subrange of the array No
Arrays.equals(a, b) Compare 1D array contents No
Arrays.deepEquals(a, b) Compare multi-dimensional array contents No
Arrays.asList(arr) Convert array to fixed-size List No
Arrays.stream(arr) Create a Stream for functional processing No

7. Common Array Operations

These operations come up frequently in real projects and coding interviews. Some have utility method shortcuts and some require manual implementation.

Finding Minimum and Maximum

import java.util.Arrays;

int[] temps = {72, 68, 75, 80, 65, 90, 85};

// Approach 1: Manual loop
int min = temps[0];
int max = temps[0];
for (int i = 1; i < temps.length; i++) {
    if (temps[i] < min) min = temps[i];
    if (temps[i] > max) max = temps[i];
}
System.out.println("Min: " + min + ", Max: " + max);
// Output: Min: 65, Max: 90

// Approach 2: Sort (modifies the array)
int[] sorted = Arrays.copyOf(temps, temps.length);
Arrays.sort(sorted);
System.out.println("Min: " + sorted[0] + ", Max: " + sorted[sorted.length - 1]);
// Output: Min: 65, Max: 90

// Approach 3: Streams (Java 8+)
int streamMin = Arrays.stream(temps).min().orElse(0);
int streamMax = Arrays.stream(temps).max().orElse(0);
System.out.println("Min: " + streamMin + ", Max: " + streamMax);
// Output: Min: 65, Max: 90

Reversing an Array

import java.util.Arrays;

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

// Swap elements from both ends toward the middle
int left = 0;
int right = numbers.length - 1;

while (left < right) {
    int temp = numbers[left];
    numbers[left] = numbers[right];
    numbers[right] = temp;
    left++;
    right--;
}

System.out.println(Arrays.toString(numbers));
// Output: [5, 4, 3, 2, 1]

Removing Duplicates

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

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

// Using a Set to remove duplicates (preserves insertion order with LinkedHashSet)
Set unique = new LinkedHashSet<>();
for (int num : withDuplicates) {
    unique.add(num);
}

// Convert back to array
int[] noDuplicates = unique.stream().mapToInt(Integer::intValue).toArray();
System.out.println(Arrays.toString(noDuplicates));
// Output: [3, 1, 4, 5, 9, 2, 6]

// Alternative: Using streams (Java 8+)
int[] streamResult = Arrays.stream(withDuplicates).distinct().toArray();
System.out.println(Arrays.toString(streamResult));
// Output: [3, 1, 4, 5, 9, 2, 6]

Merging Two Arrays

import java.util.Arrays;

int[] first = {1, 2, 3};
int[] second = {4, 5, 6, 7};

// Approach 1: System.arraycopy
int[] merged = new int[first.length + second.length];
System.arraycopy(first, 0, merged, 0, first.length);
System.arraycopy(second, 0, merged, first.length, second.length);
System.out.println(Arrays.toString(merged));
// Output: [1, 2, 3, 4, 5, 6, 7]

// Approach 2: Streams (Java 8+)
int[] streamMerged = java.util.stream.IntStream
    .concat(Arrays.stream(first), Arrays.stream(second))
    .toArray();
System.out.println(Arrays.toString(streamMerged));
// Output: [1, 2, 3, 4, 5, 6, 7]

Checking if an Element Exists

import java.util.Arrays;

String[] languages = {"Java", "Python", "JavaScript", "Go", "Rust"};

// Approach 1: Linear search with loop
boolean found = false;
for (String lang : languages) {
    if (lang.equals("Python")) {
        found = true;
        break;
    }
}
System.out.println("Found Python (loop): " + found);
// Output: Found Python (loop): true

// Approach 2: Arrays.asList().contains()
boolean exists = Arrays.asList(languages).contains("Python");
System.out.println("Found Python (asList): " + exists);
// Output: Found Python (asList): true

// Approach 3: Stream (Java 8+)
boolean streamFound = Arrays.stream(languages).anyMatch("Python"::equals);
System.out.println("Found Python (stream): " + streamFound);
// Output: Found Python (stream): true

// Approach 4: Binary search (requires sorted array)
Arrays.sort(languages);
boolean binaryFound = Arrays.binarySearch(languages, "Python") >= 0;
System.out.println("Found Python (binarySearch): " + binaryFound);
// Output: Found Python (binarySearch): true

Summing Elements

import java.util.Arrays;

double[] prices = {19.99, 35.50, 12.75, 8.25, 44.00};

// Loop approach
double total = 0;
for (double price : prices) {
    total += price;
}
System.out.printf("Total (loop): $%.2f%n", total);
// Output: Total (loop): $120.49

// Stream approach (Java 8+)
double streamTotal = Arrays.stream(prices).sum();
System.out.printf("Total (stream): $%.2f%n", streamTotal);
// Output: Total (stream): $120.49

// Average
double average = Arrays.stream(prices).average().orElse(0.0);
System.out.printf("Average price: $%.2f%n", average);
// Output: Average price: $24.10

8. Array Copying

When you assign one array variable to another, you are not copying the array -- you are copying the reference. Both variables then point to the same array in memory. To create an independent copy, you need to use one of the following techniques.

Reference Copy vs. Actual Copy

import java.util.Arrays;

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

// Reference copy -- NOT an independent copy
int[] reference = original;
reference[0] = 999;
System.out.println(Arrays.toString(original));
// Output: [999, 2, 3, 4, 5]  -- original is modified because both variables point to the same array!

// Reset
original[0] = 1;

// Actual copy -- independent array
int[] actualCopy = Arrays.copyOf(original, original.length);
actualCopy[0] = 999;
System.out.println(Arrays.toString(original));
// Output: [1, 2, 3, 4, 5]  -- original is safe
System.out.println(Arrays.toString(actualCopy));
// Output: [999, 2, 3, 4, 5]

Four Ways to Copy an Array

import java.util.Arrays;

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

// 1. Arrays.copyOf() -- simplest approach
int[] copy1 = Arrays.copyOf(source, source.length);
System.out.println("copyOf: " + Arrays.toString(copy1));
// Output: copyOf: [10, 20, 30, 40, 50]

// 2. System.arraycopy() -- fastest (native method), most flexible
int[] copy2 = new int[source.length];
System.arraycopy(source, 0, copy2, 0, source.length);
System.out.println("arraycopy: " + Arrays.toString(copy2));
// Output: arraycopy: [10, 20, 30, 40, 50]

// System.arraycopy parameters:
// (sourceArray, sourceStartIndex, destArray, destStartIndex, numberOfElements)

// 3. clone() -- inherited from Object
int[] copy3 = source.clone();
System.out.println("clone: " + Arrays.toString(copy3));
// Output: clone: [10, 20, 30, 40, 50]

// 4. Manual loop -- useful when you need to transform while copying
int[] copy4 = new int[source.length];
for (int i = 0; i < source.length; i++) {
    copy4[i] = source[i];
}
System.out.println("manual: " + Arrays.toString(copy4));
// Output: manual: [10, 20, 30, 40, 50]

Shallow Copy vs. Deep Copy

All four methods above create a shallow copy. For primitive arrays (int[], double[], etc.), a shallow copy is effectively a deep copy because primitives are values, not references. However, for object arrays, a shallow copy only copies the references -- both arrays will point to the same objects.

import java.util.Arrays;

// Shallow copy with object arrays -- dangerous
StringBuilder[] original = {
    new StringBuilder("Hello"),
    new StringBuilder("World")
};

StringBuilder[] shallowCopy = original.clone();

// Modify an object through the copy
shallowCopy[0].append(" Java");

// The original is also affected because both arrays reference the same StringBuilder objects
System.out.println(original[0]);      // Hello Java  <-- MODIFIED!
System.out.println(shallowCopy[0]);   // Hello Java

// Deep copy: you must create new objects manually
StringBuilder[] deepCopy = new StringBuilder[original.length];
for (int i = 0; i < original.length; i++) {
    deepCopy[i] = new StringBuilder(original[i].toString());
}

// Now modifying the deep copy does NOT affect the original
deepCopy[0].append(" is great");
System.out.println(original[0]);    // Hello Java       <-- unchanged
System.out.println(deepCopy[0]);    // Hello Java is great

9. Array vs ArrayList

Arrays and ArrayList are both used to store collections of elements, but they have fundamental differences. Understanding when to use each is an important skill for any Java developer.

Feature Array ArrayList
Size Fixed at creation Grows and shrinks dynamically
Primitives Yes (int[], double[], etc.) No (must use wrapper: Integer, Double)
Type safety Checked at runtime (can throw ArrayStoreException) Generics provide compile-time type checking
Performance Faster (no boxing, direct memory access) Slightly slower (autoboxing, object overhead)
Memory More compact (especially for primitives) Higher overhead (stores object references + internal array)
Length .length (field) .size() (method)
Multi-dimensional Built-in (int[][]) Nested lists (ArrayList<ArrayList<Integer>>)
Built-in methods None (use Arrays utility class) Rich API: add, remove, contains, indexOf, etc.
Null elements Allowed in object arrays Allowed
Thread safety Not synchronized Not synchronized (use Collections.synchronizedList)

When to Use Each

  • Use an array when the size is known and fixed, when you need the best performance, when working with primitives (int[] is much more efficient than ArrayList<Integer>), or when working with multi-dimensional data.
  • Use ArrayList when the size is unknown or changes frequently, when you need built-in methods like contains(), remove(), or indexOf(), when working with Java Collections Framework methods, or when you need to return a growable collection from a method.

Converting Between Arrays and ArrayLists

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;

// Array to ArrayList
String[] colorsArray = {"Red", "Green", "Blue"};
List colorsList = new ArrayList<>(Arrays.asList(colorsArray));
System.out.println("ArrayList: " + colorsList);
// Output: ArrayList: [Red, Green, Blue]

// ArrayList to Array
List fruitsList = new ArrayList<>();
fruitsList.add("Apple");
fruitsList.add("Banana");
fruitsList.add("Cherry");

String[] fruitsArray = fruitsList.toArray(new String[0]);
System.out.println("Array: " + Arrays.toString(fruitsArray));
// Output: Array: [Apple, Banana, Cherry]

// For primitive arrays, you need special handling
int[] primitiveArray = {1, 2, 3, 4, 5};

// int[] to List
List intList = new ArrayList<>();
for (int num : primitiveArray) {
    intList.add(num);  // autoboxing: int -> Integer
}
System.out.println("Integer List: " + intList);
// Output: Integer List: [1, 2, 3, 4, 5]

// Java 8+ stream approach
List streamList = Arrays.stream(primitiveArray).boxed().collect(java.util.stream.Collectors.toList());
System.out.println("Stream List: " + streamList);
// Output: Stream List: [1, 2, 3, 4, 5]

// List to int[]
int[] backToArray = intList.stream().mapToInt(Integer::intValue).toArray();
System.out.println("Back to array: " + Arrays.toString(backToArray));
// Output: Back to array: [1, 2, 3, 4, 5]

10. Passing Arrays to Methods

In Java, when you pass an array to a method, you are passing the reference by value. This means the method receives a copy of the reference that points to the same array object. As a result, any modifications the method makes to the array elements will be visible to the caller.

import java.util.Arrays;

public class ArrayMethodDemo {

    // Modifying array elements inside a method DOES affect the original
    static void doubleValues(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            arr[i] = arr[i] * 2;
        }
    }

    // Reassigning the reference does NOT affect the original
    static void tryToReplace(int[] arr) {
        arr = new int[]{99, 99, 99};  // this only changes the local copy of the reference
    }

    // Returning an array from a method
    static int[] createRange(int start, int end) {
        int[] range = new int[end - start + 1];
        for (int i = 0; i < range.length; i++) {
            range[i] = start + i;
        }
        return range;
    }

    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};

        // Modifications to elements persist
        doubleValues(numbers);
        System.out.println(Arrays.toString(numbers));
        // Output: [2, 4, 6, 8, 10]  -- original array is modified

        // Reassignment inside the method does NOT affect the original
        tryToReplace(numbers);
        System.out.println(Arrays.toString(numbers));
        // Output: [2, 4, 6, 8, 10]  -- unchanged, the method only changed its local reference

        // Getting an array back from a method
        int[] range = createRange(5, 10);
        System.out.println(Arrays.toString(range));
        // Output: [5, 6, 7, 8, 9, 10]
    }
}

Varargs (Variable-Length Arguments)

Java's varargs syntax (Type... name) lets you pass zero or more arguments of a type, and Java automatically packages them into an array. The varargs parameter must be the last parameter in the method signature.

public class VarargsDemo {

    // Varargs: accepts zero or more int arguments
    static int sum(int... numbers) {
        int total = 0;
        for (int n : numbers) {
            total += n;
        }
        return total;
    }

    // Varargs with other parameters (varargs must be last)
    static void printScores(String studentName, int... scores) {
        System.out.print(studentName + ": ");
        for (int score : scores) {
            System.out.print(score + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // Call with individual arguments
        System.out.println(sum(1, 2, 3));        // Output: 6
        System.out.println(sum(10, 20));          // Output: 30
        System.out.println(sum());                // Output: 0  (zero arguments)

        // Call with an array
        int[] values = {5, 10, 15, 20};
        System.out.println(sum(values));          // Output: 50

        // Mixed parameters
        printScores("Alice", 95, 87, 92);
        // Output: Alice: 95 87 92

        printScores("Bob", 78, 88);
        // Output: Bob: 78 88
    }
}

11. Common Mistakes

Arrays are deceptively simple. Here are the most common mistakes that trip up both beginners and experienced developers.

Mistake 1: ArrayIndexOutOfBoundsException

This is the single most common array error. It happens when you access an index that does not exist. Remember: valid indices run from 0 to length - 1.

int[] data = {10, 20, 30};

// BUG: <= causes the loop to access index 3, which does not exist
// for (int i = 0; i <= data.length; i++) {
//     System.out.println(data[i]);  // crashes on i = 3
// }

// CORRECT: use < (strictly less than)
for (int i = 0; i < data.length; i++) {
    System.out.println(data[i]);
}

// SAFEST: use for-each when you don't need the index
for (int value : data) {
    System.out.println(value);
}

Mistake 2: Comparing Arrays with == Instead of Arrays.equals()

The == operator checks if two variables point to the same object in memory. It does not compare contents.

import java.util.Arrays;

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

// BUG: == checks reference equality, not content
if (a == b) {
    System.out.println("Equal");
} else {
    System.out.println("Not equal");  // This prints, even though contents are identical
}
// Output: Not equal

// CORRECT: use Arrays.equals() for content comparison
if (Arrays.equals(a, b)) {
    System.out.println("Equal");
}
// Output: Equal

// For 2D arrays, use Arrays.deepEquals()
int[][] m1 = {{1, 2}, {3, 4}};
int[][] m2 = {{1, 2}, {3, 4}};

System.out.println(Arrays.equals(m1, m2));      // false (compares inner array references)
System.out.println(Arrays.deepEquals(m1, m2));   // true (compares actual values)

Mistake 3: Confusing length, length(), and size()

Java uses different syntax for different types. Getting them mixed up is a compile-time error.

import java.util.List;
import java.util.ArrayList;

int[] numbers = {1, 2, 3, 4, 5};
String text = "Hello";
List list = new ArrayList<>(List.of(1, 2, 3));

// Array: .length (property, no parentheses)
System.out.println(numbers.length);    // 5

// String: .length() (method, with parentheses)
System.out.println(text.length());     // 5

// List/Collection: .size() (method, with parentheses)
System.out.println(list.size());       // 3

// Common compile errors:
// numbers.length()  -- ERROR: cannot invoke length() on int[]
// numbers.size()    -- ERROR: cannot invoke size() on int[]
// text.length       -- ERROR: cannot access length field on String
// list.length       -- ERROR: cannot access length field on ArrayList

Mistake 4: Null Elements in Object Arrays

When you create an object array with new, all elements default to null. Calling a method on a null element throws a NullPointerException.

String[] names = new String[5];
names[0] = "Alice";
names[1] = "Bob";
// indices 2, 3, 4 are still null

// BUG: NullPointerException on index 2
// for (String name : names) {
//     System.out.println(name.toUpperCase());  // crashes when name is null
// }

// CORRECT: null check before using the element
for (String name : names) {
    if (name != null) {
        System.out.println(name.toUpperCase());
    }
}
// Output:
// ALICE
// BOB

Mistake 5: Forgetting That Arrays Are Zero-Indexed

If you store 5 student names, the valid indices are 0 through 4, not 1 through 5. This is especially confusing when the array represents real-world items that people number starting from 1 (rooms, seats, etc.).

// Classroom seats numbered 1 through 30
// Approach 1: Use index 0 as seat 1 (adjust when displaying)
String[] seats = new String[30];
seats[0] = "Alice";   // seat 1
seats[1] = "Bob";     // seat 2
seats[29] = "Zara";   // seat 30

for (int i = 0; i < seats.length; i++) {
    if (seats[i] != null) {
        System.out.println("Seat " + (i + 1) + ": " + seats[i]);
    }
}
// Output:
// Seat 1: Alice
// Seat 2: Bob
// Seat 30: Zara

// Approach 2: Create array with size+1 and ignore index 0
// This way seat numbers directly match indices
String[] directSeats = new String[31];  // indices 0-30, ignore 0
directSeats[1] = "Alice";
directSeats[2] = "Bob";
directSeats[30] = "Zara";
// Access: directSeats[seatNumber] -- no math needed

12. Best Practices

These guidelines will help you write array code that is clean, safe, and maintainable.

Use Enhanced For-Each When You Do Not Need the Index

The for-each loop is cleaner, eliminates off-by-one errors, and clearly communicates that you are processing every element.

String[] names = {"Alice", "Bob", "Charlie"};

// Verbose and error-prone
for (int i = 0; i < names.length; i++) {
    System.out.println(names[i]);
}

// Cleaner and safer
for (String name : names) {
    System.out.println(name);
}

Prefer ArrayList for Dynamic Sizing

If you do not know the size in advance, or if elements will be added and removed, use ArrayList instead of trying to resize arrays manually.

import java.util.ArrayList;
import java.util.List;

// BAD: manual resizing is error-prone and ugly
int[] data = new int[10];
int count = 0;
// ... later when the array is full:
// int[] bigger = new int[data.length * 2];
// System.arraycopy(data, 0, bigger, 0, data.length);
// data = bigger;

// GOOD: let ArrayList handle resizing
List dataList = new ArrayList<>();
dataList.add(42);
dataList.add(17);
// add as many elements as needed -- ArrayList resizes automatically

Use Arrays Utility Methods Instead of Reinventing the Wheel

The java.util.Arrays class has optimized, well-tested implementations for sorting, searching, copying, filling, and comparing. Always check it first before writing your own.

import java.util.Arrays;

int[] data = {5, 3, 8, 1, 9, 2, 7};

// Use built-in sort instead of writing your own
Arrays.sort(data);

// Use built-in binary search instead of writing a loop
int index = Arrays.binarySearch(data, 7);

// Use built-in toString() instead of a formatting loop
System.out.println(Arrays.toString(data));
// Output: [1, 2, 3, 5, 7, 8, 9]

// Use built-in equals() instead of element-by-element comparison
int[] other = {1, 2, 3, 5, 7, 8, 9};
System.out.println(Arrays.equals(data, other));
// Output: true

Validate Indices Before Access

When an index comes from user input, a calculation, or another method, always validate it before using it to access the array.

public static String safeGet(String[] array, int index) {
    if (array == null) {
        return "Array is null";
    }
    if (index < 0 || index >= array.length) {
        return "Index " + index + " is out of bounds (valid: 0 to " + (array.length - 1) + ")";
    }
    return array[index];
}

// Usage
String[] colors = {"Red", "Green", "Blue"};
System.out.println(safeGet(colors, 1));   // Green
System.out.println(safeGet(colors, 5));   // Index 5 is out of bounds (valid: 0 to 2)
System.out.println(safeGet(null, 0));     // Array is null

Return Empty Arrays Instead of Null

When a method returns an array but has no results, return an empty array (new String[0]) instead of null. This lets callers safely iterate without null checks.

// BAD: returning null forces every caller to check for null
public static String[] findMatchesBad(String[] items, String prefix) {
    // ... if no matches found:
    return null;
}

// Caller must check for null EVERY TIME
// String[] matches = findMatchesBad(items, "X");
// if (matches != null) {  // annoying and easy to forget
//     for (String match : matches) { ... }
// }

// GOOD: return empty array, callers can iterate safely
public static String[] findMatches(String[] items, String prefix) {
    java.util.List results = new java.util.ArrayList<>();
    for (String item : items) {
        if (item.startsWith(prefix)) {
            results.add(item);
        }
    }
    return results.toArray(new String[0]);  // returns empty array if no matches
}

// Caller never needs to check for null
String[] items = {"Apple", "Avocado", "Banana", "Cherry"};
String[] matches = findMatches(items, "A");
for (String match : matches) {
    System.out.println(match);
}
// Output:
// Apple
// Avocado

String[] noMatches = findMatches(items, "Z");
for (String match : noMatches) {
    System.out.println(match);  // loop body simply never executes
}
// (no output -- no crash, no null check needed)

13. Complete Practical Example: Student Grade Tracker

Let us tie everything together with a practical program that uses arrays for real-world data management. This StudentGradeTracker demonstrates array creation, iteration, sorting, searching, copying, statistics calculations, and working with 2D arrays.

import java.util.Arrays;

public class StudentGradeTracker {

    // Student names
    private String[] names;
    // 2D array: each row is a student, each column is an assignment grade
    private double[][] grades;
    // Number of students currently enrolled
    private int studentCount;

    public StudentGradeTracker(int maxStudents, int assignmentCount) {
        this.names = new String[maxStudents];
        this.grades = new double[maxStudents][assignmentCount];
        this.studentCount = 0;
        // Initialize all grades to -1 (ungraded)
        for (double[] row : grades) {
            Arrays.fill(row, -1.0);
        }
    }

    // Add a student and their grades
    public boolean addStudent(String name, double[] studentGrades) {
        if (studentCount >= names.length) {
            System.out.println("ERROR: Class is full. Cannot add " + name);
            return false;
        }
        if (studentGrades.length != grades[0].length) {
            System.out.println("ERROR: Expected " + grades[0].length
                + " grades, got " + studentGrades.length);
            return false;
        }
        names[studentCount] = name;
        // Copy grades (not reference) so the caller cannot modify our data
        System.arraycopy(studentGrades, 0, grades[studentCount], 0, studentGrades.length);
        studentCount++;
        return true;
    }

    // Calculate average grade for a student
    public double getStudentAverage(int studentIndex) {
        if (studentIndex < 0 || studentIndex >= studentCount) {
            return -1;
        }
        double sum = 0;
        int count = 0;
        for (double grade : grades[studentIndex]) {
            if (grade >= 0) {
                sum += grade;
                count++;
            }
        }
        return count > 0 ? sum / count : 0;
    }

    // Calculate average for a specific assignment across all students
    public double getAssignmentAverage(int assignmentIndex) {
        if (assignmentIndex < 0 || assignmentIndex >= grades[0].length) {
            return -1;
        }
        double sum = 0;
        int count = 0;
        for (int s = 0; s < studentCount; s++) {
            if (grades[s][assignmentIndex] >= 0) {
                sum += grades[s][assignmentIndex];
                count++;
            }
        }
        return count > 0 ? sum / count : 0;
    }

    // Find the highest grade across all students and assignments
    public double getHighestGrade() {
        double highest = Double.MIN_VALUE;
        for (int s = 0; s < studentCount; s++) {
            for (double grade : grades[s]) {
                if (grade > highest) {
                    highest = grade;
                }
            }
        }
        return highest;
    }

    // Find the lowest grade across all students and assignments
    public double getLowestGrade() {
        double lowest = Double.MAX_VALUE;
        for (int s = 0; s < studentCount; s++) {
            for (double grade : grades[s]) {
                if (grade >= 0 && grade < lowest) {
                    lowest = grade;
                }
            }
        }
        return lowest;
    }

    // Get students sorted by average grade (descending) -- returns a ranked copy
    public String[] getRankedStudents() {
        // Create index-average pairs
        double[] averages = new double[studentCount];
        Integer[] indices = new Integer[studentCount];
        for (int i = 0; i < studentCount; i++) {
            averages[i] = getStudentAverage(i);
            indices[i] = i;
        }

        // Sort indices by average grade (descending)
        Arrays.sort(indices, (a, b) -> Double.compare(averages[b], averages[a]));

        // Build ranked name list
        String[] ranked = new String[studentCount];
        for (int i = 0; i < studentCount; i++) {
            ranked[i] = names[indices[i]];
        }
        return ranked;
    }

    // Determine letter grade from numeric score
    private static String getLetterGrade(double average) {
        if (average >= 90) return "A";
        if (average >= 80) return "B";
        if (average >= 70) return "C";
        if (average >= 60) return "D";
        return "F";
    }

    // Print a formatted report card
    public void printReport() {
        System.out.println("=".repeat(65));
        System.out.println("          STUDENT GRADE REPORT");
        System.out.println("=".repeat(65));

        // Header row
        System.out.printf("%-12s", "Student");
        for (int a = 0; a < grades[0].length; a++) {
            System.out.printf("  HW%-3d", a + 1);
        }
        System.out.printf("  %7s  %5s%n", "Average", "Grade");
        System.out.println("-".repeat(65));

        // Student rows
        for (int s = 0; s < studentCount; s++) {
            System.out.printf("%-12s", names[s]);
            for (double grade : grades[s]) {
                if (grade >= 0) {
                    System.out.printf("  %5.1f", grade);
                } else {
                    System.out.printf("  %5s", "N/A");
                }
            }
            double avg = getStudentAverage(s);
            System.out.printf("  %7.2f  %5s%n", avg, getLetterGrade(avg));
        }

        System.out.println("-".repeat(65));

        // Assignment averages
        System.out.printf("%-12s", "Class Avg");
        for (int a = 0; a < grades[0].length; a++) {
            System.out.printf("  %5.1f", getAssignmentAverage(a));
        }
        System.out.println();

        // Overall stats
        System.out.println("-".repeat(65));
        System.out.printf("Highest grade: %.1f%n", getHighestGrade());
        System.out.printf("Lowest grade:  %.1f%n", getLowestGrade());
        System.out.println();

        // Rankings
        System.out.println("Class Rankings (by average):");
        String[] ranked = getRankedStudents();
        for (int i = 0; i < ranked.length; i++) {
            int studentIndex = -1;
            for (int s = 0; s < studentCount; s++) {
                if (names[s].equals(ranked[i])) {
                    studentIndex = s;
                    break;
                }
            }
            double avg = getStudentAverage(studentIndex);
            System.out.printf("  %d. %-12s  %.2f (%s)%n", i + 1, ranked[i], avg, getLetterGrade(avg));
        }
        System.out.println("=".repeat(65));
    }

    // --- Main method: demonstrate the tracker ---
    public static void main(String[] args) {
        // Create tracker for up to 10 students with 5 assignments
        StudentGradeTracker tracker = new StudentGradeTracker(10, 5);

        // Add students with their assignment grades
        tracker.addStudent("Alice",   new double[]{92, 88, 95, 90, 87});
        tracker.addStudent("Bob",     new double[]{78, 82, 75, 80, 85});
        tracker.addStudent("Charlie", new double[]{95, 97, 93, 98, 96});
        tracker.addStudent("Diana",   new double[]{88, 72, 80, 85, 90});
        tracker.addStudent("Edward",  new double[]{65, 70, 68, 72, 60});

        // Print the full report
        tracker.printReport();
    }
}

Program Output

=================================================================
          STUDENT GRADE REPORT
=================================================================
Student       HW1    HW2    HW3    HW4    HW5   Average  Grade
-----------------------------------------------------------------
Alice         92.0   88.0   95.0   90.0   87.0    90.40      A
Bob           78.0   82.0   75.0   80.0   85.0    80.00      B
Charlie       95.0   97.0   93.0   98.0   96.0    95.80      A
Diana         88.0   72.0   80.0   85.0   90.0    83.00      B
Edward        65.0   70.0   68.0   72.0   60.0    67.00      D
-----------------------------------------------------------------
Class Avg     83.6   81.8   82.2   85.0   83.6
-----------------------------------------------------------------
Highest grade: 98.0
Lowest grade:  60.0

Class Rankings (by average):
  1. Charlie       95.80 (A)
  2. Alice         90.40 (A)
  3. Diana         83.00 (B)
  4. Bob           80.00 (B)
  5. Edward        67.00 (D)
=================================================================

What This Example Demonstrates

  • 1D arraynames[] stores student names
  • 2D arraygrades[][] stores each student's assignment scores (rows = students, columns = assignments)
  • Arrays.fill() — Initializes all grades to -1 (ungraded)
  • System.arraycopy() — Safely copies grades to prevent external modification
  • Nested iteration — Traversing a 2D array to find highest/lowest grades and compute averages
  • Arrays.sort() with a custom comparator — Ranking students by average grade
  • Index validation — Checking bounds before accessing elements
  • Returning arrays from methodsgetRankedStudents() returns a new String array
  • Passing arrays to methodsaddStudent() accepts a double array parameter

 




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published. Required fields are marked *