Tutorials

The Java Tutorial: Arrays

Key Takeaways

  • Java arrays have a fixed length and use indexes starting at zero.
  • Use the length field, not length(), and keep indexes below array.length.
  • Array assignment shares a reference; copying creates a separate array, but reference elements are still shared.
  • Arrays.sort changes the supplied array. Arrays.binarySearch requires input sorted according to the same ordering.

Java arrays are objects that hold a fixed number of elements with one declared component type. You access elements by index, starting at zero, and use java.util.Arrays for common tasks such as copying, sorting, searching, and comparing contents.

This tutorial builds on Java primitive data types. You'll start with a character array, then work through loops, arrays of arrays, and the utility methods you'll use most often.

Creating and Accessing Java Arrays

Declare an array and assign its elements

Write the component type followed by square brackets: char[] means an array of characters. Declaring a variable alone doesn't create an array; new char[5] creates an array with five elements.

public class ArrayBasics {
    public static void main(String[] args) {
        char[] myArray = new char[5];
        myArray[0] = 'a';
        myArray[1] = 'b';
        myArray[2] = 'c';
        myArray[3] = 'd';
        myArray[4] = 'e';

        System.out.println(myArray[3]); // d
        System.out.println(myArray.length); // 5
    }
}

The expression myArray[3] reads the fourth element. Assigning myArray[0] = 'a'; writes to the first element. Think of the index as an offset from the beginning: zero selects the first position.

Java array length and valid indexes

An array exposes its size through the length field, without parentheses. For a nonempty array, valid indexes run from 0 through array.length - 1; an empty array has length zero and no valid element indexes.

index:           0    1    2    3    4       5
value:          'a'  'b'  'c'  'd'  'e'    invalid
myArray.length = 5
last valid index = 4

Invalid reads and writes throw ArrayIndexOutOfBoundsException at runtime. For this example, both myArray[-1] and myArray[5] are invalid accesses, even though those expressions can compile.

Initialize arrays with values or defaults

An array initializer creates an array whose length matches the number of listed values. Bare braces work in a declaration, but a later assignment needs an array creation expression such as new char[]{'x', 'y'}; having an array-typed variable on the left isn't enough.

// Fragment: inside main.
char[] letters = {'a', 'b', 'c', 'd', 'e'};
letters = new char[]{'x', 'y'};

// letters = {'x', 'y'}; // Does not compile.

When you allocate an array without listing values, Java initializes its elements automatically:

  • byte, short, int, and long elements start at zero; float and double elements start at positive zero.
  • boolean elements start at false.
  • char elements start at the zero character, '\u0000', not the character '0'.
  • Reference elements, including String elements and array references, start at null.

These defaults apply to array elements. A local declaration such as int[] numbers; doesn't initialize the local variable itself; you must assign it before using it.

Initialize a large array without listing every value

Allocate once when you know the required length. If the default element values are what you need, no explicit filling is necessary; use Arrays.fill for a repeated nondefault value, or a loop for values calculated from each index.

import java.util.Arrays;

public class FillArray {
    public static void main(String[] args) {
        int[] scores = new int[1_000];
        Arrays.fill(scores, -1);

        System.out.println(scores[0]); // -1
        System.out.println(scores[999]); // -1
    }
}

For application code, validate a requested length before allocating when that length comes from user input. Set an application-appropriate upper bound rather than allowing a request to dictate an arbitrarily large allocation.

Looping Through a Java Array

Use an indexed for loop when you need positions or want to replace elements. Use an enhanced for loop when you only need each value. The safe indexed condition is i < array.length, not i <= array.length.

// Fragment: inside main.
int[] numbers = {10, 20, 30};

for (int i = 0; i < numbers.length; i++) {
    numbers[i] += 1;
    System.out.println(i + ": " + numbers[i]);
}

for (int value : numbers) {
    System.out.println(value);
}

The first loop changes the elements to 11, 21, and 31. The second loop reads those updated values. For more on loop conditions and branching, see Java control flow statements.

Multidimensional Arrays in Java

A Java two-dimensional array is an array whose elements refer to other arrays. Its rows can have different lengths; this is called a jagged array.

public class JaggedArray {
    public static void main(String[] args) {
        char[][] myArray = {
            {'a', 'b', 'c', 'd'},
            {'e', 'f', 'g'}
        };

        System.out.println(myArray[1][2]); // g
        System.out.println(myArray.length); // 2
        System.out.println(myArray[0].length); // 4
        System.out.println(myArray[1].length); // 3

        for (int row = 0; row < myArray.length; row++) {
            for (int column = 0; column < myArray[row].length; column++) {
                System.out.print(myArray[row][column]);
            }
            System.out.println();
        }
    }
}

The outer array has two elements. myArray[1][2] selects the second row and then its third character, 'g'. Traversal must use each row's own length rather than assuming every row matches the first.

Allocating only the outer array, as in new char[2][], leaves its row references null. Allocate those rows before indexing into them; otherwise, accessing a row's elements throws NullPointerException.

Using the java.util.Arrays Class

Copy an array instead of sharing its reference

Assignment copies the array reference, not its elements. Two variables can therefore refer to the same array; changing an element through either variable changes that shared array.

import java.util.Arrays;

public class CopyArray {
    public static void main(String[] args) {
        int[] original = {1, 2, 3};
        int[] alias = original;
        int[] copy = Arrays.copyOf(original, original.length);

        alias[0] = 9;

        System.out.println(Arrays.toString(original)); // [9, 2, 3]
        System.out.println(Arrays.toString(copy)); // [1, 2, 3]
    }
}

Arrays.copyOf creates a new array. For reference arrays, it copies references rather than duplicating the referenced objects: this is a shallow copy. Copying a two-dimensional array this way shares its existing row arrays.

Copy a range or copy into an existing destination

Arrays.copyOfRange(original, from, to) creates a new array using a half-open range: from is inclusive and to is exclusive. The range [1, 3) therefore selects indexes 1 and 2.

import java.util.Arrays;

public class CopyRange {
    public static void main(String[] args) {
        char[] myArray = {'a', 'b', 'c', 'd'};
        char[] copyArray = Arrays.copyOfRange(myArray, 1, 3);

        System.out.println(new String(copyArray)); // bc
        System.out.println(Arrays.toString(copyArray)); // [b, c]

        char[] destination = new char[2];
        System.arraycopy(myArray, 1, destination, 0, 2);
        System.out.println(Arrays.toString(destination)); // [b, c]
    }
}

System.arraycopy(source, sourcePosition, destination, destinationPosition, count) writes into an existing destination. Its last argument is an element count, not an exclusive end index. Neither copying method is universally preferable: choose a new range-sized array or an existing destination according to what your code needs.

Search a sorted array with Arrays.binarySearch

Binary search requires the array, or searched range, to be sorted first. For object arrays sorted with a comparator, search using the same ordering.

import java.util.Arrays;

public class SearchArray {
    public static void main(String[] args) {
        char[] letters = {'d', 'a', 'c', 'b'};
        Arrays.sort(letters);

        for (char key : new char[]{'c', 'e'}) {
            int result = Arrays.binarySearch(letters, key);

            if (result >= 0) {
                System.out.println(key + " found at " + result);
            } else {
                int insertionPoint = -result - 1;
                System.out.println(key + " missing; insertion point " + insertionPoint);
            }
        }
    }
}

This prints c found at 2 and e missing; insertion point 4. A missing value produces -(insertionPoint) - 1, not necessarily -1; here the raw result for 'e' is -5. Decode it with -result - 1, and never use a negative result as an array index.

The range overload, such as Arrays.binarySearch(letters, 1, 3, 'c'), searches from index 1 inclusive to index 3 exclusive and returns an index in the original array. With duplicates, binary search doesn't guarantee which matching index it returns.

Compare contents with Arrays.equals and Arrays.deepEquals

For character arrays, Arrays.equals checks equal lengths and equal characters in corresponding positions. Element order matters.

import java.util.Arrays;

public class CompareArrays {
    public static void main(String[] args) {
        char[] array1 = {'a', 'b', 'c', 'd'};
        char[] array2 = {'a', 'b', 'c', 'd'};
        char[] array3 = {'a', 'c', 'b', 'd'};

        System.out.println(Arrays.equals(array1, array2)); // true
        System.out.println(Arrays.equals(array1, array3)); // false

        char[][] left = {{'a', 'b'}, {'c'}};
        char[][] right = {{'a', 'b'}, {'c'}};

        System.out.println(Arrays.equals(left, right)); // false
        System.out.println(Arrays.deepEquals(left, right)); // true
    }
}

Calling array1.equals(array2) compares array identity rather than contents. For nested arrays, Arrays.equals compares the inner-array references; Arrays.deepEquals descends into the nested arrays to compare their contents.

Sort numeric arrays and object arrays

Arrays.sort changes the supplied array. Integer-array overloads sort in ascending numeric order; character-array overloads sort by character value. Object-array overloads also support a Comparator, which defines a custom ordering, but primitive-array overloads don't accept one.

import java.util.Arrays;
import java.util.Comparator;

public class SortArrays {
    public static void main(String[] args) {
        int[] myArray = {33, 55, 23, 64, 2};
        Arrays.sort(myArray);

        System.out.println(myArray[0]); // 2
        System.out.println(Arrays.toString(myArray)); // [2, 23, 33, 55, 64]

        String[] words = {"apple", "pear", "banana"};
        Arrays.sort(words, Comparator.reverseOrder());
        System.out.println(Arrays.toString(words)); // [pear, banana, apple]
    }
}

If another part of your program needs the original order, copy the array before sorting. Don't sort shared input as a hidden side effect merely to make one search possible.

Array Types, Compatible Values, and Runtime Checks

An array has one component type, but reference elements can hold compatible subtypes. An Object[], for example, can contain both strings and other objects.

Reference arrays are covariant: a String[] can be assigned to an Object[] variable. The actual array remains a String[], so Java checks element stores at runtime.

// Fragment: inside main; demonstrates an intentional runtime failure.
Object[] values = new String[2];
values[0] = "hello";
values[1] = new Object(); // Throws ArrayStoreException.

Prefer the most specific useful array type. Don't treat an Object[] variable as permission to insert arbitrary objects when its underlying array has a narrower component type.

Java Arrays vs ArrayList

Use arrays when a fixed element count fits the problem, especially when you want primitive elements directly. Choose ArrayList when adding and removing elements is part of normal operation. ArrayList is resizable and uses reference types; storing integers means using ArrayList<Integer>, with boxing between int and Integer.

RequirementArrayArrayList
Number of elementsFixed after creationCan grow or shrink
Primitive valuesDirectly, such as int[]Through wrappers, such as Integer
Element countarray.lengthlist.size()

Assigning a newly allocated array to the same variable doesn't resize the original array; it replaces that variable's reference. For the collection alternative, continue with the Java Collections Framework and Java wrapper classes and autoboxing.

FAQ

How do you declare and initialize an array in Java?

Use int[] numbers = new int[3] to allocate three zero-initialized elements, or int[] numbers = {10, 20, 30} to supply values immediately. For a later assignment, use numbers = new int[]{10, 20, 30}; bare braces alone aren't valid there.

How do you get the length of a Java array?

Read array.length, without parentheses. It gives the number of elements, so the last valid index of a nonempty array is array.length - 1.

How do you print the contents of an array in Java?

Use Arrays.toString(array) for a one-dimensional array and Arrays.deepToString(array) for nested arrays. A char[] can also be printed directly with System.out.print because PrintStream has a dedicated character-array overload.

Does Arrays.binarySearch require a sorted array?

Yes. The searched array or range must already be sorted according to the same ordering used by the search. A nonnegative result is a matching index; a negative result encodes the insertion point as -(insertionPoint) - 1.

What is the difference between an array and an ArrayList?

An array has a fixed length and can store primitive values or object references. An ArrayList can grow and shrink, but its element type must be a reference type, so primitive values use wrappers such as Integer.

Further Reading

Written by Sam Erickson

Software engineer. I write every article on StackChief myself. Author of Mastering the Tech Interview. More about me