Java Tutorial » Chapter 16 — Arrays

Chapter 16 — Java Arrays

Mastering the fundamentals of storing, manipulating, and processing collections of data with built-in utilities.

1. Introduction to Arrays

What is a Java Array?

An array is a container object that holds a fixed number of values of a single type. The length of an array is established when the array is created, and after creation, its length is fixed.

Each item in an array is called an element, and each element is accessed by its numerical index. Indexing begins at 0. For example, the 5th element of an array would be at index 4.

In Java, arrays are objects. This means they are created on the heap memory, and the variable you declare holds a reference to the array object. Because they are objects, they can be manipulated like other objects (e.g., passed to methods).

Key Properties:

  • Fixed Size: You cannot change the size of an array after it's created.
  • Homogeneous Data: All elements must be of the same data type (e.g., all `int` or all `String`).
  • Zero-Based Indexing: The first element is at index `0`, and the last is at `length - 1`.
2. How Arrays Store Values

Memory Representation of Arrays

Understanding how arrays store values in memory is crucial for efficient programming. In Java, arrays are stored in contiguous memory locations, which allows for fast access to elements using their index.

Visualizing Array Storage

Let's visualize how an integer array is stored in memory:

Index:
0
1
2
3
4
Values:
10
25
7
42
18

When you declare an array variable, it stores a reference to the array object in memory. The array object itself contains the actual data elements in contiguous memory locations.

Memory Layout

0x1000
int[] numbers = new int[5];
0x2000
numbers[0] = 10
0x2004
numbers[1] = 25
0x2008
numbers[2] = 7
0x200C
numbers[3] = 42
0x2010
numbers[4] = 18

The array variable at address 0x1000 contains a reference (0x2000) to the actual array data. Each element occupies 4 bytes (for an int) in contiguous memory locations. This contiguous storage allows for O(1) access time to any element using its index.

When you access an element with numbers[2], Java calculates the memory address as 0x2000 + (2 × 4) = 0x2008 and retrieves the value at that location.

3. Declaring & Creating Arrays

Two-Step Process

Creating an array is a two-step process: first, you declare a variable that will hold the array reference, and second, you use the `new` operator to allocate memory for the array object itself.

Step 1: Declaration

This tells the compiler that the variable will refer to an array of a certain type.

// Preferred style
dataType[] arrayRefVar;

// C/C++ style, also works in Java
dataType arrayRefVar[];

Example: `int[] myNumbers;` or `String[] names;`

Step 2: Instantiation

This creates the array object in memory with a specified size.

arrayRefVar = new dataType[arraySize];

Example: `myNumbers = new int[10];` creates an array that can hold 10 integers.

Combining Declaration and Instantiation

int[] myNumbers = new int[10]; // An array for 10 integers
String[] names = new String[5]; // An array for 5 String objects

Default Values: When an array is created, its elements are automatically assigned default values:

  • Numeric types (`int`, `double`, etc.): `0` or `0.0`
  • `boolean`: `false`
  • Object types (`String`, etc.): `null`
4. Initializing & Accessing

Populating and Reading Elements

You can assign values to individual elements using their index, enclosed in square brackets `[]`.

public class InitializeAccess {
    public static void main(String[] args) {
        int[] scores = new int[4];
        scores[0] = 95;
        scores[1] = 88;
        scores[2] = 76;
        scores[3] = 100;

        // Accessing an element
        System.out.println("The first score is: " + scores[0]);

        // Trying to access an invalid index will cause an error
        // System.out.println(scores[4]); // Throws ArrayIndexOutOfBoundsException
    }
}

Array Literal Initialization

You can create and initialize an array in a single step using an array literal. The size is automatically determined by the number of elements.

public class ArrayLiteral {
    public static void main(String[] args) {
        // The array size is 3
        String[] names = {"Alice", "Bob", "Charlie"};

        System.out.println("Second name: " + names[1]);
        System.out.println("Array length: " + names.length);
    }
}
5. Processing Arrays

Iterating with a Standard `for` Loop

A standard `for` loop is ideal when you need to know the index of the element you are processing. The `length` field (not a method!) of an array tells you how many elements it can hold.

public class ProcessArray {
    public static void main(String[] args) {
        double[] prices = {19.99, 5.50, 12.75, 99.95};

        // Find the sum of all prices
        double total = 0;
        for (int i = 0; i < prices.length; i++) {
            total += prices[i];
        }
        System.out.println("Total price: $" + total);

        // Find the most expensive item
        double maxPrice = prices[0];
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > maxPrice) {
                maxPrice = prices[i];
            }
        }
        System.out.println("Highest price: $" + maxPrice);
    }
}
6. The For-Each Loop

Enhanced for-loop

JDK 1.5 introduced a more concise way to loop through all the elements of an array: the enhanced for-loop, or for-each loop.

This loop is preferred when you only need to read elements and don't need to know their index.

public class ForEachLoop {
    public static void main(String[] args) {
        String[] fruits = {"Apple", "Banana", "Cherry"};

        // Print all the array elements
        System.out.println("My favorite fruits:");
        for (String fruit : fruits) {
            System.out.println("- " + fruit);
        }
    }
}

Important: You cannot use a for-each loop to modify the array's elements, as the loop variable (`fruit` in the example) is a copy of the value, not a reference to the array slot.

7. Multidimensional Arrays

Arrays of Arrays

Multidimensional arrays are simply arrays whose elements are other arrays. The most common type is a two-dimensional (2D) array, which is often used to represent tables or matrices.

Declaring and Creating a 2D Array

// Declares a 2D array of integers
int[][] matrix;

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

Initializing a 2D Array

public class MultiArray {
    public static void main(String[] args) {
        // Initializing with a literal
        int[][] matrix = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12}
        };

        // Accessing an element: [row][column]
        System.out.println("Element at row 1, column 2: " + matrix[1][2]); // Outputs 7
    }
}

Processing a 2D Array

You typically use nested `for` loops to iterate through a 2D array.

public class ProcessMultiArray {
    public static void main(String[] args) {
        int[][] matrix = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };

        // Iterate through all elements
        for (int i = 0; i < matrix.length; i++) { // Outer loop for rows
            for (int j = 0; j < matrix[i].length; j++) { // Inner loop for columns
                System.out.print(matrix[i][j] + "\t");
            }
            System.out.println(); // New line after each row
        }
    }
}
8. Passing Arrays to Methods

Arrays as Method Parameters

Arrays are passed to methods by reference. This means the method receives a reference to the original array, not a copy. Any changes made to the array inside the method will permanently affect the original array.

public class PassArrayToMethod {
    // Method to print an array
    public static void printArray(int[] arr) {
        for (int x : arr) {
            System.out.print(x + " ");
        }
        System.out.println();
    }

    // Method that modifies the array
    public static void scaleArray(int[] arr, int factor) {
        for (int i = 0; i < arr.length; i++) {
            arr[i] = arr[i] * factor;
        }
    }

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

        System.out.print("Original array: ");
        printArray(numbers);

        scaleArray(numbers, 2); // Scale the array by a factor of 2

        System.out.print("Array after scaling: ");
        printArray(numbers); // The original array is modified
    }
}
9. Returning an Array from a Method

Arrays as Return Values

A method can return an array. The return type of the method must be declared as an array type (e.g., `int[]`). This is useful for methods that generate or process data and need to return a collection of values.

public class ReturnArrayFromMethod {
    // This method returns a new array containing the first n prime numbers
    public static int[] getFirstNPrimes(int n) {
        int[] primes = new int[n];
        int num = 2;
        int count = 0;
        while (count < n) {
            if (isPrime(num)) {
                primes[count] = num;
                count++;
            }
            num++;
        }
        return primes;
    }

    // Helper method to check for primality
    public static boolean isPrime(int num) {
        if (num <= 1) return false;
        for (int i = 2; i <= Math.sqrt(num); i++) {
            if (num % i == 0) return false;
        }
        return true;
    }

    public static void main(String[] args) {
        int[] firstFivePrimes = getFirstNPrimes(5);
        System.out.println("First 5 prime numbers:");
        for (int prime : firstFivePrimes) {
            System.out.print(prime + " "); // 2 3 5 7 11
        }
    }
}
10. The `java.util.Arrays` Class

Built-in Utility Functions

Java provides a very useful helper class, `java.util.Arrays`, which contains various static methods for manipulating arrays (such as sorting and searching). You must import it to use: `import java.util.Arrays;`

Arrays.toString()

A convenient way to get a string representation of an array's contents. It's much better than writing your own loop for printing.

import java.util.Arrays;

public class ArraysToString {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30, 40};
        System.out.println(numbers); // Prints something like [I@1b6d3586
        System.out.println(Arrays.toString(numbers)); // Prints [10, 20, 30, 40]
    }
}

Arrays.sort()

Sorts the elements of an array into ascending order. It uses a highly optimized, dual-pivot quicksort algorithm for primitives and Timsort for objects.

import java.util.Arrays;

public class ArraysSort {
    public static void main(String[] args) {
        int[] numbers = {40, 10, 30, 20};
        System.out.println("Before sort: " + Arrays.toString(numbers));
        
        Arrays.sort(numbers);
        
        System.out.println("After sort: " + Arrays.toString(numbers));
    }
}

Arrays.binarySearch()

Searches for a specified value in a sorted array using the binary search algorithm. This is very fast (O(log n)) but requires the array to be sorted first. It returns the index of the element if found, or a negative value if not found.

import java.util.Arrays;

public class ArraysBinarySearch {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30, 40, 50};
        int index = Arrays.binarySearch(numbers, 30);
        System.out.println("Index of 30 is: " + index); // 2

        int notFoundIndex = Arrays.binarySearch(numbers, 35);
        System.out.println("Index of 35 is: " + notFoundIndex); // A negative number
    }
}

Arrays.fill()

Assigns the specified value to each element of the array. Useful for initialization. You can fill the entire array or a specific range of elements.

import java.util.Arrays;

public class ArraysFill {
    public static void main(String[] args) {
        int[] data = new int[5];
        Arrays.fill(data, -1); // Fill the entire array with -1
        System.out.println(Arrays.toString(data)); // [-1, -1, -1, -1, -1]

        // Fill a specific range (from index 1 to 3, exclusive of 3)
        Arrays.fill(data, 1, 3, 99);
        System.out.println(Arrays.toString(data)); // [-1, 99, 99, -1, -1]

        double[] prices = new double[3];
        Arrays.fill(prices, 9.99);
        System.out.println(Arrays.toString(prices)); // [9.99, 9.99, 9.99]
    }
}

Arrays.equals()

Returns `true` if two arrays are equal in size and their corresponding elements are equal. It's the correct way to compare array contents, as the `==` operator only checks if they are the same object in memory.

import java.util.Arrays;

public class ArraysEquals {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        int[] b = {1, 2, 3};
        int[] c = a;

        System.out.println(a == b); // false (different objects)
        System.out.println(a == c); // true (same object reference)
        System.out.println(Arrays.equals(a, b)); // true (same contents)
        
        // For multidimensional arrays, use deepEquals()
        int[][] matrix1 = {{1, 2}, {3, 4}};
        int[][] matrix2 = {{1, 2}, {3, 4}};
        System.out.println(Arrays.equals(matrix1, matrix2)); // false (compares references)
        System.out.println(Arrays.deepEquals(matrix1, matrix2)); // true (compares contents)
    }
}

Arrays.compare()

Compares two arrays lexicographically (element by element). It returns:

  • A negative integer if the first array is lexicographically less than the second
  • Zero if the arrays are equal
  • A positive integer if the first array is lexicographically greater than the second
This method was added in Java 9.

import java.util.Arrays;

public class ArraysCompare {
    public static void main(String[] args) {
        int[] arr1 = {1, 2, 3};
        int[] arr2 = {1, 2, 4};
        int[] arr3 = {1, 2, 3};
        
        System.out.println(Arrays.compare(arr1, arr2)); // Negative (arr1 < arr2)
        System.out.println(Arrays.compare(arr2, arr1)); // Positive (arr2 > arr1)
        System.out.println(Arrays.compare(arr1, arr3)); // 0 (arr1 == arr3)
        
        // For multidimensional arrays, use compare() on each dimension or deepEquals()
        int[][] matrix1 = {{1, 2}, {3, 4}};
        int[][] matrix2 = {{1, 2}, {3, 5}};
        System.out.println(Arrays.compare(matrix1, matrix2)); // Negative (matrix1 < matrix2)
    }
}

Arrays.copyOf() and Arrays.copyOfRange()

Creates a new array that is a copy of the original array. `copyOf()` copies the entire array or truncates/pads it to the specified length. `copyOfRange()` copies a specific range of elements.

import java.util.Arrays;

public class ArraysCopy {
    public static void main(String[] args) {
        int[] original = {1, 2, 3, 4, 5};
        
        // Copy entire array
        int[] copy1 = Arrays.copyOf(original, original.length);
        System.out.println(Arrays.toString(copy1)); // [1, 2, 3, 4, 5]
        
        // Copy with new length (padded with zeros if longer)
        int[] copy2 = Arrays.copyOf(original, 7);
        System.out.println(Arrays.toString(copy2)); // [1, 2, 3, 4, 5, 0, 0]
        
        // Copy with truncation
        int[] copy3 = Arrays.copyOf(original, 3);
        System.out.println(Arrays.toString(copy3)); // [1, 2, 3]
        
        // Copy a range of elements (from index 1 to 4, exclusive of 4)
        int[] copy4 = Arrays.copyOfRange(original, 1, 4);
        System.out.println(Arrays.toString(copy4)); // [2, 3, 4]
    }
}
11. Practice & Challenge

Test Your Skills

  1. Write a program to find the average of an array of numbers.
  2. Write a program to check if an array contains a specific value (without using `java.util.Arrays`).
  3. Write a program to find the index of an array element.
  4. Write a program to remove a specific element from an array (by creating a new, smaller array).
  5. Write a program to find the sum of the diagonals of a square 2D matrix.

🏆 Challenge: Array Analyzer

Create a program that analyzes an array of integers provided by the user. The program should use `java.util.Arrays` methods where applicable and display the following:

  • The original array.
  • The sorted array.
  • The minimum and maximum values.
  • Ask the user for a number and check if it exists in the array (using `binarySearch`).

import java.util.Arrays;
import java.util.Scanner;

public class ArrayAnalyzer {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.println("--- Array Analyzer ---");
        System.out.print("Enter numbers separated by spaces: ");
        String line = scanner.nextLine();
        String[] numberStrings = line.split(" ");

        int[] numbers = new int[numberStrings.length];
        for (int i = 0; i < numberStrings.length; i++) {
            numbers[i] = Integer.parseInt(numberStrings[i]);
        }

        // 1. Original array
        System.out.println("\nOriginal Array: " + Arrays.toString(numbers));

        // 2. Sorted array
        int[] sortedNumbers = Arrays.copyOf(numbers, numbers.length); // Create a copy to sort
        Arrays.sort(sortedNumbers);
        System.out.println("Sorted Array:   " + Arrays.toString(sortedNumbers));

        // 3. Min and Max
        int min = sortedNumbers[0];
        int max = sortedNumbers[sortedNumbers.length - 1];
        System.out.println("Minimum value: " + min);
        System.out.println("Maximum value: " + max);

        // 4. Binary Search
        System.out.print("\nEnter a number to search for: ");
        int target = scanner.nextInt();
        int index = Arrays.binarySearch(sortedNumbers, target);

        if (index >= 0) {
            System.out.println("Found " + target + " at index " + index + " in the sorted array.");
        } else {
            System.out.println(target + " was not found in the array.");
        }

        scanner.close();
    }
}