Topic 3.10
The Arrays Utility Class
In one line
java.util.Arrays is a toolbox of ready-made static methods for arrays: print, sort, search, fill, copy, compare, and turn an array into a list or stream. Using it is shorter, faster and less buggy than writing the same loops yourself.
Think of it like this
A kitchen drawer of tools. You could cut a pizza with a kitchen knife, open a tin with a screwdriver and measure flour with a mug, but a pizza wheel, a tin opener and a measuring cup do each job faster and better. Arrays is that drawer for arrays: one tool for each common job, already tested by millions of programs.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Utility class
- A class that only holds static helper methods and is never instantiated, such as
ArraysorMath. - In place
- Changing the array you passed in, rather than returning a new one.
Arrays.sortsorts in place. - Binary search
- Finding a value in a sorted array by checking the middle and discarding the half that can't contain it, again and again.
- Insertion point
- The index where a missing key would go to keep the array sorted.
binarySearchencodes it in its negative result. - Stable sort
- A sort that keeps equal elements in their original order.
Arrays.sorton objects is stable; on primitives stability doesn't matter. - Comparator
- An object that decides the order of two values, used to sort in a custom way (Topic 9.10).
- Fixed-size list
- A list whose elements can be replaced but not added or removed.
Arrays.asListreturns one. - Lexicographic order
- Dictionary order: compare first elements, and only if equal look at the next ones.
Step by step
01Print and compare without loops
Arrays.toString(nums) returns a readable string like [42, 7, 19]. You'll use it constantly while learning and debugging. For a 2D array, Arrays.deepToString (Topic 3.9).
Arrays.equals(a, b) is true when both arrays have the same length and equal elements in the same order. For arrays of objects it uses each element's equals method (Topic 4.8).
02Sorting: what algorithm, and what it costs
Arrays.sort(int[]) and the other primitive versions use a dual-pivot quicksort, O(n log n) on average and very fast in practice. Equal primitives are indistinguishable, so stability doesn't matter.
Arrays.sort(Object[]) (strings, records, any objects) uses TimSort, a merge sort variant that is stable and O(n log n) in the worst case, and close to O(n) on input that's already almost sorted.
Both sort in place: they change your array and return nothing. If you need the original order too, copy first: int[] sorted = nums.clone(); Arrays.sort(sorted);. Strings sort by character codes, so uppercase letters come before lowercase; pass String.CASE_INSENSITIVE_ORDER to ignore case.
Arrays.sort(a, from, to) sorts only indexes from to to - 1. Like most Java ranges, the start is included and the end is excluded.
03Binary search and its negative answer
Arrays.binarySearch(a, key) halves the search range each step, so a million elements need about 20 comparisons instead of up to a million. It only works on a sorted array; on unsorted input the answer is meaningless, and there is no error.
If found, it returns the index (with duplicates, any matching index). If not found it returns -(insertionPoint) - 1. The - 1 exists so that "not found, would go at index 0" is -1, distinct from "found at index 0".
So binarySearch([3, 7, 19, 25, 42], 20) returns -4: 20 would be inserted at index 3, and -(3) - 1 = -4. To recover the position: insertionPoint = -result - 1. How binary search works inside, and its many variants, is the DSA course's Binary Search module (/dsa/binary-search).
04Filling and copying
Arrays.fill(a, 7) sets every element to 7, useful for initialising a table with something other than 0, such as Integer.MAX_VALUE for shortest-distance tables or -1 for "not computed yet" in memoisation.
Arrays.copyOf(a, n) returns a new array of length n: shorter truncates, longer pads with default values. copyOfRange(a, from, to) returns the slice from from up to, not including, to.
Arrays.setAll(a, i -> i * i) fills each element from its index using a lambda (Topic 10.1).
05asList: a list view, not a copy
Arrays.asList(arr) returns a List backed by the array. Setting an element in the list changes the array, and changing the array changes the list. No elements are copied.
The list has the array's fixed size, so add and remove throw UnsupportedOperationException. For a normal, growable list, copy it: new ArrayList<>(Arrays.asList(arr)). For an unmodifiable list use List.of(...) (Java 9, Topic 9.11).
Remember the trap from Topic 3.5: Arrays.asList(int[]) gives a list with one element, the int[] itself.
06Java 8 and 9 additions
Arrays.stream(a) (Java 8) returns an IntStream, LongStream, DoubleStream or Stream<T>, so Arrays.stream(nums).sum() or .max() are one-liners. Streams are Phase 10. Arrays.parallelSort (Java 8) uses several CPU cores for large arrays.
Arrays.mismatch(a, b) (Java 9) returns the first index where two arrays differ, or -1 if they're equal. Arrays.compare(a, b) (Java 9) compares lexicographically and returns a negative, zero or positive number, like String.compareTo.
Try it yourself
- 1
Decode binarySearch results
On the sorted array
[3, 7, 19, 25, 42], predict the result of searching for 1, 30 and 50 before running. (Insertion points 0, 4 and 5 give-1,-5and-6.) - 2
Sort a copy, keep the original
Modify "Print, sort and search" so it prints both the original order and the sorted order at the end. You'll need
nums.clone()orArrays.copyOfbefore sorting. - 3
Use fill for a memo table
In the memoised Fibonacci of Topic 3.7,
memo[n] != 0was used to mean "computed". Rewrite it withArrays.fill(memo, -1)andmemo[n] != -1. Why is that more robust? (A legitimately computed 0 would no longer be mistaken for "not computed".)
Code & diagrams
Expected output
before: [42, 7, 19, 3, 25]
sorted: [3, 7, 19, 25, 42]
index of 19: 2
search 20: -4 (insert at 3)
natural: [Banana, apple, pear]
ignore case: [apple, Banana, pear]
range sort: [9, 6, 7, 8, 5]Expected output
fill: [7, 7, 7, 7]
copyOf 3: [1, 2, 3]
copyOf 7: [1, 2, 3, 4, 5, 0, 0]
copyOfRange 1..4: [2, 3, 4]
src == other: false
Arrays.equals: true
setAll: [0, 1, 4, 9, 16, 25]
sum via stream: 15Expected output
array after set: [z, b, c]
list after array change: [z, b, q]
add failed: fixed-size list
copy: [z, b, q, d]
mismatch: 2
mismatch with copy: -1
a before b: trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Add to a list from Arrays.asList
Call add on the list returned by Arrays.asList.
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
list.add(4);
}
}Break #2
Binary search an unsorted array
Call binarySearch on {5, 1, 4, 2, 3} without sorting.
int[] u = {5, 1, 4, 2, 3};
System.out.println(Arrays.binarySearch(u, 1));
System.out.println(Arrays.binarySearch(u, 4));Myth vs fact
Myth
Arrays.sort returns a sorted copy.
Fact
It sorts the array you pass, in place, and returns void. Copy first if you need the original order.
Myth
Arrays.asList copies the array into a new ArrayList.
Fact
It returns a fixed-size view backed by the array. Changes flow both ways, and add/remove throw.
Myth
binarySearch returns -1 when the key is missing.
Fact
It returns -(insertionPoint) - 1, which is -1 only when the key would go at index 0.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Since JDK 14, the primitive dual-pivot quicksort falls back to heap sort when recursion gets too deep, guaranteeing O(n log n), and for small ranges uses insertion sort. Object sorting uses TimSort, which detects already-sorted runs; an inconsistent
Comparatorcan make it throwIllegalArgumentException: Comparison method violates its general contract!. - ▸
Arrays.equals,mismatchandfillon primitive arrays are backed by vectorised JDK code (ArraysSupport.vectorizedMismatchand intrinsics), so they're usually faster than an equivalent hand-written loop. - ▸
Arrays.asListreturns the private classjava.util.Arrays$ArrayList, notjava.util.ArrayList. It allowssetandnullelements;List.of(Java 9) forbids both and is truly unmodifiable. Know which one an API returns before you mutate it. - ▸
Arrays.hashCode(a)is content-based and matchesList.hashCodefor the same elements, whilea.hashCode()is identity-based. Using an array as aHashMapkey therefore uses identity, a frequent bug; wrap it in aListor record (Topic 9.4).
Remember this
- 1
Arrayslives injava.util, so addimport java.util.Arrays;. Every method isstatic: you call it on the class,Arrays.sort(nums), passing the array as an argument. You never create anArraysobject. - 2
Viewing and comparing:
toString(a)gives[3, 7, 19];deepToStringdoes the same for nested arrays.equals(a, b)compares contents (anddeepEqualsfor nested arrays).hashCode(a)gives a content-based hash. - 3
Sorting and searching:
sort(a)sorts in place in ascending order (a range version sorts part of the array).binarySearch(a, key)finds a key in a sorted array in O(log n) steps; if the key is missing it returns a negative number that encodes where it would go. - 4
Filling and copying:
fill(a, v)sets every element;copyOf(a, newLength)copies and pads or truncates;copyOfRange(a, from, to)copies a slice (toexcluded).setAll(a, i -> ...)(Java 8) computes each element from its index. - 5
Bridges to other APIs:
asList(...)wraps an object array as a fixed-sizeListthat writes through to the array;stream(a)(Java 8) opens a stream for sums, filters and more (Topic 10.4). Java 9 addedmismatch(first differing index) andcompare(dictionary order of two arrays). - 6
Each method is overloaded for every primitive type and for objects, which is why you can call
Arrays.sorton anint[], adouble[]or aString[]. Sorting objects uses their natural order (Comparable) or aComparatoryou pass in (Topic 9.10).
Explain it without notes
Which algorithms does Arrays.sort use for primitives and for objects, and why the difference?
Explain the return value of Arrays.binarySearch when the key is not found.
What's the difference between Arrays.asList(arr) and new ArrayList<>(Arrays.asList(arr))?
Why must an array be sorted before calling binarySearch, and what happens if it isn't?
Practice
Given {88, 42, 95, 61, 73}, print the scores sorted, the lowest, the highest and the median, using Arrays methods.
Write a program that checks whether two words are anagrams by sorting their characters: convert with toCharArray(), sort both, compare with Arrays.equals. Test "listen"/"silent" and "java"/"lava".
Given the sorted array {10, 20, 30, 40}, use binarySearch to insert 25 at the correct position, producing a new array [10, 20, 25, 30, 40].
Trade-offs
- ↔
Arraysmethods are tested, optimised and short, but they work on fixed-size arrays. Once you need to add and remove elements, move to collections (Phase 9), which offer similar helpers inCollectionsandList. - ↔
Sorting once (O(n log n)) and then binary searching many times (O(log n) each) beats repeated linear searches, but if you only search once, a single linear scan (O(n)) is cheaper than sorting.
- ↔
Arrays.asListis a cheap view with no copying, but its fixed size and write-through behaviour surprise people. Copy when you need independence.
Done when you can
Done when you can print, compare, fill and copy arrays with
Arraysinstead of loops.Done when you can sort whole arrays and ranges, and sort strings case-insensitively.
Done when you can decode a negative
binarySearchresult into an insertion point.Done when you can explain why
Arrays.asListlists can't grow and how to get one that can.Done when you know which sort algorithm is used for primitives vs objects and why.