Introduction

In this post, we will learn about the static utility methods in the Arrays class in Java.

Arrays Utility class

The Arrays Utility class in Java has various methods for working with arrays (like sorting and searching). This also has a static factory method that allows arrays to be viewed as lists.

Arrays asList method

The Arrays#asList static factory method takes a var-args of arguments and returns a fixed-size list which is backed by the passed array (var-args). Hence, if we make any change to the array, it will be visible in the returned list and vice versa.

The list it returns implements the optional Collection methods, except those that change the size of the returned list. If we call any method which changes the list, it will throw an UnsupportedOperationException.

This method acts as a bridge between the array-based and collection-based APIs.

In the below example, we pass several elements to the asList method. It returns a list wrapping these elements. In other words, it enables us to create a fixed-sized list initialized to contain several elements.

List<String> strings = Arrays.asList("a", "b", "c");
System.out.println(strings); // [a, b, c]

The result is the same if we had passed an array, as shown below. Hence, it is also helpful to wrap an existing array as a list.

String[] stringArr = new String[]{"a", "b", "c"};
strings = Arrays.asList(stringArr);
System.out.println(strings); //[a, b, c]

Modifiability of the asList returned list

In the above returned list, if we call say the add method, it will throw an UnsupportedOperationException.

//throws //java.lang.UnsupportedOperationException
strings.add("aa");

But the returned list is modifiable i.e., we can change the elements at an index. Let us update the string element at index 0.

//The list returned by this method is modifiable
strings.set(0, "aa");
System.out.println(strings); //[aa, b, c]
System.out.println(Arrays.toString(stringArr)); //[aa, b, c]

This not only updates the strings list but also the original array (stringArr) from which we created the list.

To create an unmodifiable list, we can use Collections.unmodifiableList. Now, calling the set method will throw UnsupportedOperationException.

String[] stringArr = new String[]{"a", "b", "c"};
List<String> strings = Collections.unmodifiableList(Arrays.asList(stringArr));
System.out.println(strings); //[a, b, c]

strings.set(0, "aa"); //java.lang.UnsupportedOperationException

Arrays binarySearch method

We can use the binarySearch method to do binary search on a passed array, i.e., searching for a value in the array using binary search. If the array is not sorted, the results are undefined.

If the array has multiple elements with the same value, it could find any one of them.

Calling the binarySearch method will return the index of the searched key if the key is present. If the key is not present, then it will return the value of (-(insertion point) - 1). The insertion point is the index in the array where the searched key could be inserted. Hence, if the key is present, it returns a value >=0.

The binarySearch method has several overloaded methods for supporting various kinds of arrays like int[], long[] and Object[] etc. For discussion, I’ll use an int[] only.

Let us create a simple int[] and do binary search. Remember that the array has to be sorted.

int[] ints = new int[]{1, 2, 4, 5, 6, 7};
System.out.println(Arrays.binarySearch(ints, 2)); //1
System.out.println(Arrays.binarySearch(ints, 3)); //-3

In the first call, we search for the key 2. Since it is present in the array, it returns the index of key 2 (which is 1).

In the second example, we search for a key which doesn’t exist in the array. In this case, it will use the insertion point i.e., if 3 is inserted into the array, it would be at index 2. Hence it returns -3 (i.e., -insertionPoint - 1).

System.out.println(Arrays.binarySearch(ints, 0)); //-1
System.out.println(Arrays.binarySearch(ints, 8)); //-7
  • Searching for 0, it returns - 1 (insertion point would be index 0).
  • Searching for 8, it returns -7 (insertion point would be index 6).

Binary Search within a range

This is an overloaded binarySearch method which takes a range i.e., fromIndex (inclusive) and toIndex (exclusive) and performs the binary search only within this range. The method signature is shown below,

public static int binarySearch(int[] a, int fromIndex, int toIndex, int key)

In the above example int[], if we search for the key 2 within the range [0, 3) (index 0, 1 and 2), it will return index 1 as the result.

On the other hand, if we search for key 2 within [2, 5) (considers index 2, 3 and 4 only), it won’t find the key 2 within this range (the considered sub-array is {4, 5, 6, 7}). This will return -3 as the result because the insertion point is index 2 (the head of the considered sub-array).

System.out.println(Arrays.binarySearch(ints, 0, 3, 2)); //1
System.out.println(Arrays.binarySearch(ints, 2, 5, 2)); //-3

Note:

  • If fromIndex > toIndex, then it will throw an IllegalArgumentException.
  • If fromIndex < 0 or toIndex > array.length, then it will throw an ArrayIndexOutOfBoundsException.

Binary Search using a comparator

There is a special method taking an array of reference type elements (T[]) and hence to compare we need to pass a comparator.

Let us create a simple Student class as shown below.

public class Student {
    private int id;
    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

Let us use Comparator.comparing method to define a comparator. Here, we compare two students by their ids - if the ids are same, we compare using their names.

Comparator<Student> studentComparator = Comparator.comparing(Student::getId)
        .thenComparing(Student::getName);

Using this to do binary search,

System.out.println(Arrays.binarySearch(students, new Student(4, "Adam"), studentComparator)); //2
System.out.println(Arrays.binarySearch(students, new Student(3, "John"), studentComparator)); //-3
System.out.println(Arrays.binarySearch(students, new Student(4, "Mary"), studentComparator)); //-4

In the first call, we search passing a Student instance whose name is Adam and id as 4. Since such a student instance is present at index 2, it returns it.

In the second call, we search for John with id 3. Since no student with id 3 exists, it returns -3 (insertion point of a student object with id 3 would be at index 2 and hence it returned -3).

Finally, we are searching for Mary with id 4. Note that a student with id=4 and name=Adam exists. Since in the comparator, we use the name for ordering if the id is the same, student with name Mary (id=4) would be inserted after Adam in the array and hence the insertion point is index 3. Thus, it returned -4 as the result.

Binary Search using a comparator within a range

We can also pass a range (fromIndex and toIndex) to the above overloaded method variant to do binary search within a range. The method signature is shown below, but I’ll skip the example as it is similar to the other ones we’ve seen.

public static <T> int binarySearch(T[] a, int fromIndex, int toIndex,
                                       T key, Comparator<? super T> c)

Binary search on an Object[]

Let us use the same Student[] binary search example. This time, let us call the binarySearch overload taking an Object[]. For this, the Student instances must implement the Comparable interface. If they don’t, we will get a ClassCastException when calling the binarySearch method.

public class Student implements Comparable<Student> {
    private int id;
    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @Override
    public int compareTo(Student o) {
        return Integer.compare(id, o.id);
    }
}

Unlike the previous example, here the comparison happens solely based on the id.

Making similar calls as previous example, we get the same results.

System.out.println(Arrays.binarySearch(students, new Student(4, "Adam"))); //2
System.out.println(Arrays.binarySearch(students, new Student(3, "John"))); //-3

But the last call has a different behaviour now!!

Since we are using only the id for comparing/ordering (no fallback on the name field), when we search for a student with id 4, it will do a match with Adam because he has the same id. So, it returns index 2 as the response.

System.out.println(Arrays.binarySearch(students, new Student(4, "Mary"))); //2

Arrays fill

There are several overloaded fill methods supporting arrays of primitive types (like int[], long[] etc.,) and Object[].

For each of them, there are two overloaded fill methods which we will look at.

Arrays fill the entire array

This method takes an array and a value and assigns the specified value to each element in the passed array. Example, considering an int[],

int[] ints = new int[]{1, 2, 3, 4};
Arrays.fill(ints, 0);
System.out.println(Arrays.toString(ints)); //[0, 0, 0, 0]

In the above example, we created an int[] with values 1 through 4. We then called the Arrays#fill method with the int[] and value to be filled as 0. It then initialized each element of the int[] to 0.

Arrays filling a range

There is another overloaded method which takes a fromIndex (inclusive) and a toIndex (exclusive) and fills the values only in this range.

Arrays.fill(ints, 1, 3, 10);
System.out.println(Arrays.toString(ints)); //[0, 10, 10, 0]

In the above example, we are filling the array from index 1 to 2 (inclusive) to value 10.

Here, if the fromIndex and toIndex are equal, then the range will be empty.

//from == to - nothing filled
Arrays.fill(ints, 1, 1, 100);
System.out.println(Arrays.toString(ints)); //[0, 10, 10, 0]
  • If the fromIndex > toIndex, it will throw an IllegalArgumentException.
  • If fromIndex < 0 or toIndex > a.length it will throw an ArrayIndexOutOfBoundsException.

Arrays copyOf and copyOfRange

Arrays copyOf

The copyOf method takes an array and a length and copies the passed array into a new array and returns it. As seen earlier for the other methods, there are several overloads to support various array types.

In the below example, we have an int[]. We copy it to get a new array.

int[] ints = new int[]{1, 2, 3, 4};
int[] copiedInts = Arrays.copyOf(ints, 4);
System.out.println(Arrays.toString(copiedInts)); //[1, 2, 3, 4]

It will truncate or pad with zeros if necessary to make the copy have the specified length.

If we specify it to copy to length 2, it copies only the first two elements. If we specify a greater length, the rest of the elements will be 0 (the default value depends on the array type).

//truncating
copiedInts = Arrays.copyOf(ints, 2);
System.out.println(Arrays.toString(copiedInts)); //[1, 2]

//padding
copiedInts = Arrays.copyOf(ints, 6);
System.out.println(Arrays.toString(copiedInts)); //[1, 2, 3, 4, 0, 0]

Arrays copyOfRange

The copyOfRange method takes an array and a range, i.e, from (inclusive) and to (exclusive) and it copies the specified range into the new array.

Here, we copy the elements of the passed array from range 1 (inclusive) to 3 (exclusive).

int[] ints = new int[]{1, 2, 3, 4};
int[] copiedInts = Arrays.copyOfRange(ints, 1, 3);
System.out.println(Arrays.toString(copiedInts)); //[2, 3]

If we specify a value for to which is greater than the array length, the rest of the elements will be padded.

int[] copiedInts = Arrays.copyOfRange(ints, 1, 6);
System.out.println(Arrays.toString(copiedInts)); //[2, 3, 4, 0, 0]

It throws:

  • ArrayIndexOutOfBoundsException – if from < 0 or from > original.length
  • IllegalArgumentException – if from > to

Arrays mismatch

This has been added since Java 9.

The Arrays#mismatch method returns the index of the first mismatch between the two passed arrays. If there is no mismatch, it returns -1.

It has overloads supporting arrays having primitive elements, Object[] and T[].

Comparing two int[]s, in the first call, there is a mismatch at index 2. In the second example, both arrays are equal and hence there is no mismatch.

int[] intArr1 = new int[]{1, 2, 3, 4};
int[] intArr2 = new int[]{1, 2, 5, 4};
System.out.println(Arrays.mismatch(intArr1, intArr2)); //2

intArr1 = new int[]{1, 2, 3, 4};
intArr2 = new int[]{1, 2, 3, 4};
System.out.println(Arrays.mismatch(intArr1, intArr2)); //-1

In the below two examples, we have mismatches at index 0 and 3, respectively.

intArr1 = new int[]{1, 2, 3, 4};
intArr2 = new int[]{10, 2, 3, 4};
System.out.println(Arrays.mismatch(intArr1, intArr2)); //0
intArr1 = new int[]{1, 2, 3, 4};
intArr2 = new int[]{1, 2, 3, 40};
System.out.println(Arrays.mismatch(intArr1, intArr2)); //3

Both arrays need not be of the same size. Shown below are few examples.

intArr1 = new int[]{1, 2, 3, 4, 5, 6};
intArr2 = new int[]{1, 2, 3, 40};
System.out.println(Arrays.mismatch(intArr1, intArr2)); //3

intArr1 = new int[]{1, 2, 3, 4, 5};
intArr2 = new int[]{1, 2, 3, 4};
System.out.println(Arrays.mismatch(intArr1, intArr2)); //4

In the last example, the second arrays is a proper prefix of the first array and hence the returned index is applicable only for the larger array.

For calling the Object[] parameter overload, the type must implement the Comparable interface. Using a String in the below example,

String[] strArr1 = new String[]{"a", "b", "c"};
String[] strArr2 = new String[]{"a", "b", "d"};
System.out.println(Arrays.mismatch(strArr1, strArr2)); //2

The below example demonstrates calling the mismatch method taking T[].

Student[] students1 = new Student[]{
        new Student(1, "Zac"),
        new Student(2, "Michael"),
};
Student[] students2 = new Student[]{
        new Student(1, "Zac"),
        new Student(2, "Adam")
};
Comparator<Student> studentComparator = Comparator.comparing(Student::getId)
        .thenComparing(Student::getName);
System.out.println(Arrays.mismatch(students1, students2, studentComparator)); //1

Other than the ones shown, it has overloads where we can specify the range for each of the passed arrays as well. Here, we pass the range as [0, 2) considering the indexes 0 and 1 only. Hence, as the two arrays are equal for that sub-range, it returns -1.

String[] strArr1 = new String[]{"a", "b", "c"};
String[] strArr2 = new String[]{"a", "b", "d"};
System.out.println(Arrays.mismatch(strArr1, 0, 2, strArr2, 0, 2)); //-1

If we consider the full first array, then it returns index 2 as the result.

System.out.println(Arrays.mismatch(strArr1, 0, 3, strArr2, 0, 2)); //2

Comparing two arrays

The compare method was also added in Java 9.

It compares two arrays lexicographically. It uses the mismatch method to find the index at which the mismatch happens and compares the two elements at that index. For an int[], it does,

int i = Arrays.mismatch(a, b);
if (i >= 0 && i < Math.min(a.length, b.length))
    return Integer.compare(a[i], b[i]);
return a.length - b.length;

In the below example, the first mismatch is at index 2. Comparing 3 and 30, 3 is smaller and hence it returns -1, which implies that the first array is lexicographically smaller than the second. The second example is inverse of this, where it returns 1.

int[] intArr1 = new int[]{1, 2, 3, 4};
int[] intArr2 = new int[]{1, 2, 30, 4};
System.out.println(Arrays.compare(intArr1, intArr2)); //-1

intArr1 = new int[]{1, 20, 3, 4};
intArr2 = new int[]{1, 2, 3, 4};
System.out.println(Arrays.compare(intArr1, intArr2)); //1

If both arrays are equal, it returns 0.

intArr1 = new int[]{1, 2, 3, 4};
intArr2 = new int[]{1, 2, 3, 4};
System.out.println(Arrays.compare(intArr1, intArr2)); //0

If one array is a proper prefix of the other, it just compares their array lengths.

intArr1 = new int[]{1, 2, 3, 4, 5};
intArr2 = new int[]{1, 2, 3, 4};
System.out.println(Arrays.compare(intArr1, intArr2)); //1

intArr1 = new int[]{1, 2, 3, 4};
intArr2 = new int[]{1, 2, 3, 4, 5};
System.out.println(Arrays.compare(intArr1, intArr2)); //-1

There are overloads for a reference array (T[]) and T[] with a comparator. In the below example, the first array is smaller than the second and hence it returns -1 (comparing strings “c” and “d”).

String[] strArr1 = new String[]{"a", "b", "c"};
String[] strArr2 = new String[]{"a", "b", "d"};
System.out.println(Arrays.compare(strArr1, strArr2)); //-1

An example using the comparator is shown below.

Student[] students1 = new Student[]{
        new Student(1, "Zac"),
        new Student(2, "Michael"),
};
Student[] students2 = new Student[]{
        new Student(1, "Zac"),
        new Student(2, "Adam")
};
Comparator<Student> studentComparator = Comparator.comparing(Student::getId)
        .thenComparing(Student::getName);
System.out.println(Arrays.compare(students1, students2, studentComparator)); //1

As seen with the mismatch examples, we can also pass a range for both the arrays in all the overloaded compare methods.

Arrays setAll

There are four overloaded setAll methods supporting int[], long[], double[] and T[].

The setAll method takes an array and a generator function and sets all the elements of the array using the generator function. The type of the generator function will vary depending on the array type. We will explore this in the below examples.

setAll - int[]

The setAll for int[] uses an IntUnaryOperator as the generator function. An IntUnaryOperator takes a primitive int argument and returns a primitive int result. The generator function gets the index of the array (not the value) and the value the function returns will be the final value at that index.

Let us create a generator function which when getting an index, will add 10 to the current value at that index and returns it.

int[] intArr = new int[]{1, 2, 3};
IntUnaryOperator intUnaryOperator = i -> intArr[i] + 10;
Arrays.setAll(intArr, intUnaryOperator);
System.out.println(Arrays.toString(intArr)); //[11, 12, 13]

The internal implementation of setAll is equivalent of,

IntStream.range(startInclusive, endExclusive)
          .forEach(i -> array[i] = generator.applyAsInt(i));

setAll - long[]

The setAll method for long[] takes an IntToLongFunction (it takes a primitive int and returns a primitive long value).

long[] longArr = new long[]{1, 2, 3};
IntToLongFunction intToLongFunction = i -> longArr[i] + 10;
Arrays.setAll(longArr, intToLongFunction);
System.out.println(Arrays.toString(longArr)); //[11, 12, 13]

setAll - double[]

Similar to the previous ones, this uses an IntToDoubleFunction to generate a double from an int value.

double[] doubleArr = new double[]{1.1, 2.2, 3.3};
IntToDoubleFunction intToDoubleFunction = i -> doubleArr[i] + 10;
Arrays.setAll(doubleArr, intToDoubleFunction);
System.out.println(Arrays.toString(doubleArr)); //[11.1, 12.2, 13.3]

setAll - T[]

The setAll overload for a T[] takes an IntFunction to map the index to an instance of T. In the below example, on getting the index, we convert the string to its uppercase.

String[] stringArr = new String[]{"a", "b", "c"};
IntFunction<String> intFunction = i -> stringArr[i].toUpperCase();
Arrays.setAll(stringArr, intFunction);
System.out.println(Arrays.toString(stringArr)); //[A, B, C]

Arrays - parallelSetAll

The Arrays utility class also has parallelSetAll method. It is similar to the setAll method but does it parallelly. The implementation is equivalent of,

 IntStream.range(startInclusive, endExclusive)
          .parallel()
          .forEach(i -> array[i] = generator.apply(i));

Example:

String[] stringArr = new String[]{"a", "b", "c"};
IntFunction<String> intFunction = i -> stringArr[i].toUpperCase();
Arrays.parallelSetAll(stringArr, intFunction);
System.out.println(Arrays.toString(stringArr)); //[A, B, C]

Arrays sort

This is one of the most used utility method from the Arrays class. We can either sort an entire array or a range.

Sorting arrays of primitive values

To sort an entire array with primitive values, we pass the array to the Arrays utility class sort method.

int[] intArr = new int[]{3, 4, 2, 1};
Arrays.sort(intArr);
System.out.println(Arrays.toString(intArr)); //[1, 2, 3, 4]

double[] doubleArr = new double[]{10.1, 1.34, 1.33, 2.5};
Arrays.sort(doubleArr);
System.out.println(Arrays.toString(doubleArr)); //[1.33, 1.34, 2.5, 10.1]

To sort only a sub-array or a range, we can pass the fromIndex (inclusive) and toIndex(exclusive) which represent the range of the array to sort.

In the below example, we sort only the middle two double values. The rest of the elements will remain in their original place.

double[] doubleArr = new double[]{10.1, 1.34, 1.33, 2.5};
Arrays.sort(doubleArr, 1, 3);
System.out.println(Arrays.toString(doubleArr)); //[10.1, 1.33, 1.34, 2.5]

Sorting an Object[]

I’ve used a String[] to demonstrate sorting an Object[]. We call this overload when the type implements the Comparable interface. If it doesn’t, then it will throw a ClassCastException when sorting.

String[] stringArr = new String[] {"e", "c", "a", "b"};
Arrays.sort(stringArr);
System.out.println(Arrays.toString(stringArr)); //[a, b, c, e]

To sort a subarray,

String[] stringArr = new String[] {"e", "c", "a", "b"};
Arrays.sort(stringArr, 1, 3);
System.out.println(Arrays.toString(stringArr)); //[e, a, c, b]

Sorting a T[]

If the array element type doesn’t implement the Comparable interface, then we call this overload taking a T[] and pass a Comparator for sorting the array elements. Using the same example used for the binarySearch and I’ve added the below toString implementation to the Student class.

@Override
public String toString() {
    return "Student{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
}

Let us now create an array of Student instances and sort it using a Comparator.

Student[] students = new Student[]{
        new Student(5, "Penny"),
        new Student(2, "Michael"),
        new Student(4, "Adam"),
        new Student(1, "Zac"),
        new Student(6, "Penny")
};

Comparator<Student> studentComparator = Comparator.comparing(Student::getId)
        .thenComparing(Student::getName);

Arrays.sort(students, studentComparator);
System.out.println(Arrays.toString(students));

Prints,

[Student{id=1, name='Zac'}, Student{id=2, name='Michael'}, Student{id=4, name='Adam'}, Student{id=5, name='Penny'}, Student{id=6, name='Penny'}]

Arrays parallelSort

The Arrays utility class has parallelSort method which makes use of parallel sorting. It breaks the array into sub-arrays and sorts each and merges them back to get the full sorted array.

Creating a stream from an array

We can use the stream method to create a stream containing the elements of the array. This is covered in the Seven Ways to Create a Stream in Java post.

Arrays parallelPrefix

The parallelPrefix method cumulates, in parallel, each element of the array in place using the passed function. The function must be a side-effect-free, associative function to perform the cumulation.

For example, let us say we have an int[] and the function is to do addition of two passed int values. Then, at each index, it writes the value of the previous element and the current element. If the arrays is [2, 1, 0, 3], the result will be [2, 3, 3, 6].

There are four main overloads for the parallelPrefix method for an int[], long[], double[] and T[].

parallelPrefix examples

For an int[], it takes an IntBinaryOperator as the op function. It takes two primitive int values and returns an int. Here, we use the method reference Integer::sum - it is the equivalent of a lambda expression which sums two int values.

int[] intArray = new int[]{1, 2, 3, 5, 10};
IntBinaryOperator intBinaryOperator = Integer::sum;
Arrays.parallelPrefix(intArray, intBinaryOperator);
System.out.println(Arrays.toString(intArray)); //[1, 3, 6, 11, 21]

For long[] and double[], it takes a LongBinaryOperator and DoubleBinaryOperator, respectively.

long[] longArray = new long[]{1L, 2L, 3L, 5L, 10L};
LongBinaryOperator longBinaryOperator = Long::sum;
Arrays.parallelPrefix(longArray, longBinaryOperator);
System.out.println(Arrays.toString(longArray)); //[1, 3, 6, 11, 21]
double[] doubleArray = new double[]{1.1, 2.1, 3.2, 5.5, 10.0};
DoubleBinaryOperator doubleBinaryOperator = Double::sum;
Arrays.parallelPrefix(doubleArray, doubleBinaryOperator);
System.out.println(Arrays.toString(doubleArray)); //[1.1, 3.2, 6.4, 11.9, 21.9]

For an array of reference types, it takes a BinaryOperator for the op function. In this example, it takes two strings and concatenates them and returns it.

String[] arr = new String[]{"a", "b", "c", "d", "e"};
BinaryOperator<String> binaryOperator = (left, right) -> left + right;
Arrays.parallelPrefix(arr, binaryOperator);
System.out.println(Arrays.toString(arr)); //[a, ab, abc, abcd, abcde]

For each of the methods we have seen, we can pass the range as well - fromIndex (inclusive) and toIndex (exclusive). Examples are shown below.

int[] intArray = new int[]{1, 2, 3, 5, 10};
intBinaryOperator = Integer::sum;
Arrays.parallelPrefix(intArray, 2, 4, intBinaryOperator);
System.out.println(Arrays.toString(intArray)); //[1, 2, 3, 8, 10]

long[] longArray = new long[]{1L, 2L, 3L, 5L, 10L};
longBinaryOperator = Long::sum;
Arrays.parallelPrefix(longArray, 2, 4, longBinaryOperator);
System.out.println(Arrays.toString(longArray)); //[1, 2, 3, 8, 10]

double[] doubleArray = new double[]{1.1, 2.1, 3.2, 5.5, 10.0};
doubleBinaryOperator = Double::sum;
Arrays.parallelPrefix(doubleArray, 2, 4, doubleBinaryOperator);
System.out.println(Arrays.toString(doubleArray)); //[1.1, 2.1, 3.2, 8.7, 10.0]

String[] arr = new String[]{"a", "b", "c", "d", "e"};
binaryOperator = (left, right) -> left + right;
Arrays.parallelPrefix(arr, 2, 4, binaryOperator);
System.out.println(Arrays.toString(arr)); //[a, b, c, cd, e]

Arrays equals and deepEquals

equals

The equals method takes two arrays and checks if they are equal. Two arrays are equal if both arrays have the same number of elements and all the corresponding pairs of elements in two arrays are equal (i.e., if they have the same elements in the same order).

There are several overloaded methods for supporting arrays containing primitive elements, Object[] and T[].

int[] intArr1 = new int[]{1, 2, 3};
int[] intArr2 = new int[]{1, 2, 3};
System.out.println(Arrays.equals(intArr1, intArr2)); //true

//Change an element
intArr2[2] = 22;
System.out.println(Arrays.equals(intArr1, intArr2)); //false

When calling the overload taking Object[], we have to make sure the type (String class in the below example) implements equals and hashCode methods.

String[] stringArr1 = new String[]{"a", "b", "c"};
String[] stringArr2 = new String[]{"a", "b", "c"};
System.out.println(Arrays.equals(stringArr1, stringArr2)); //true

The overload for comparing two T[] takes a Comparator for comparing the elements.

Student[] students1 = new Student[]{
        new Student(1, "Zac"),
        new Student(2, "Michael")
};
Student[] students2 = new Student[]{
        new Student(1, "Zac"),
        new Student(2, "Michael")
};

Comparator<Student> studentComparator = Comparator.comparing(Student::getId)
        .thenComparing(Student::getName);
System.out.println(Arrays.equals(students1, students2, studentComparator)) //true 

There are overloaded equals method which allow us to pass a range and enable us to check for equals only within that range. I’ll skip the examples for these.

Arrays deepEquals

When we have a nested array (of arbitrary depth) and when we call the equals(Object[], Object[]), it just compares the corresponding pair of elements from both the arrays by calling the equals method on them. Hence, when we have a nested array, this wouldn’t work as shown below.

int[][] nestedIntArr1 = new int[][]{
        {1, 2, 3},
        {4, 5, 6}
};
int[][] nestedIntArr2 = new int[][]{
        {1, 2, 3},
        {4, 5, 6}
};
System.out.println(Arrays.equals(nestedIntArr1, nestedIntArr2)); //false

In such cases, we can use the deepEquals method.

System.out.println(Arrays.deepEquals(nestedIntArr1, nestedIntArr2)); //true

Conclusion

In this post, we learnt in detail about the various methods in the Arrays utility class in Java. Hope this was helpful.