Java Arrays Class: Sort, Search & Compare Like a Pro
Every Java application handles data, and arrays are one of the most fundamental ways to hold a collection of that data. Whether you're sorting a leaderboard of player scores, searching through product IDs in an e-commerce system, or pre-filling a configuration grid with default values, you're going to reach for arrays constantly. The trap most developers fall into is writing their own loops for tasks that Java already solves — faster, safer, and in a single line.
What Is the Arrays Class and Why Does It Exist?
The Arrays class lives in java.util and is a collection of static utility methods. You never instantiate it — you just call Arrays.sort(), Arrays.fill(), Arrays.copyOf(), and so on, directly. It was introduced because these operations are so universally needed that building them into the JDK means everyone benefits from highly optimized, battle-tested implementations.
Under the hood, Arrays.sort() on primitives uses a dual-pivot Quicksort algorithm tuned by JDK engineers. Arrays.sort() on objects uses TimSort, a hybrid sorting algorithm that's exceptionally fast on partially sorted data. You'd spend weeks getting anywhere near that performance writing your own version.
Think of the Arrays class as the difference between buying a professional chef's knife versus carving with a butter knife. Both cut food. One does it in a way that professionals actually rely on.
The key insight: every method in Arrays is static. You never write new Arrays(). You just use it like a function library scoped to array operations.
import java.util.Arrays; public class ArraysClassIntro { public static void main(String[] args) { // A raw, unsorted array of employee IDs from a database dump int[] employeeIds = {4021, 1003, 5678, 2200, 9001, 1003, 3344}; // Arrays.toString() converts the array to a readable string // Without this, printing the array directly gives a memory address System.out.println("Before sort: " + Arrays.toString(employeeIds)); // Arrays.sort() sorts IN PLACE — it modifies the original array // Uses dual-pivot Quicksort for primitives: O(n log n) average Arrays.sort(employeeIds); System.out.println("After sort: " + Arrays.toString(employeeIds)); // Arrays.binarySearch() ONLY works correctly on a sorted array // Returns the index of the element, or a negative number if not found int indexOfTarget = Arrays.binarySearch(employeeIds, 3344); System.out.println("Index of employee 3344: " + indexOfTarget); // Searching for a value that doesn't exist int missingIndex = Arrays.binarySearch(employeeIds, 9999); System.out.println("Index of employee 9999 (missing): " + missingIndex); } }
After sort: [1003, 1003, 2200, 3344, 4021, 5678, 9001]
Index of employee 3344: 3
Index of employee 9999 (missing): -8
Copying Arrays: copyOf vs copyOfRange vs clone
One of the most common errors in Java is thinking that assigning an array to a new variable creates a copy. It doesn't. It creates a second reference to the same array. If you sort the 'copy', the original sorts too. Arrays gives you three proper ways to copy, each suited to a different scenario.
Arrays.copyOf(original, newLength) creates a new array from the start of the original up to newLength. If newLength is larger than the source, the extra slots are zero-filled for primitives and null for objects — making it perfect for growing an array.
Arrays.copyOfRange(original, from, to) lets you slice a portion of an array, like extracting the top 5 from a sorted leaderboard. The 'from' index is inclusive, 'to' is exclusive.
clone() is a native method inherited from Object that also produces a shallow copy. It's fine for primitives but be careful with arrays of objects — each element reference is copied, not the object itself. For most use cases, prefer copyOf because it's explicit about length and intent.
import java.util.Arrays; public class ArrayCopyingStrategies { public static void main(String[] args) { int[] monthlyRevenue = {12000, 8500, 15000, 9300, 17800, 14200}; // --- WRONG WAY: Reference copy, NOT a data copy --- int[] wrongCopy = monthlyRevenue; wrongCopy[0] = 99999; // This ALSO changes monthlyRevenue[0]! System.out.println("After wrong copy mutation: " + Arrays.toString(monthlyRevenue)); // Reset for the demo monthlyRevenue[0] = 12000; // --- RIGHT WAY 1: Arrays.copyOf --- // Copies the full array; extra length slots are zero-filled int[] fullCopy = Arrays.copyOf(monthlyRevenue, monthlyRevenue.length); fullCopy[0] = 99999; // Does NOT affect monthlyRevenue System.out.println("Original after fullCopy mutation: " + Arrays.toString(monthlyRevenue)); // Grow the array by 3 extra slots (they will be 0) int[] grownCopy = Arrays.copyOf(monthlyRevenue, monthlyRevenue.length + 3); System.out.println("Grown copy: " + Arrays.toString(grownCopy)); // --- RIGHT WAY 2: Arrays.copyOfRange --- // Extract Q1 data (months 0, 1, 2) — 'to' index is EXCLUSIVE int[] q1Revenue = Arrays.copyOfRange(monthlyRevenue, 0, 3); System.out.println("Q1 revenue: " + Arrays.toString(q1Revenue)); // Extract Q2 data (months 3, 4, 5) int[] q2Revenue = Arrays.copyOfRange(monthlyRevenue, 3, 6); System.out.println("Q2 revenue: " + Arrays.toString(q2Revenue)); // --- RIGHT WAY 3: clone() --- // Shallow copy — safe for primitives, be careful with objects int[] clonedRevenue = monthlyRevenue.clone(); System.out.println("Cloned: " + Arrays.toString(clonedRevenue)); } }
Original after fullCopy mutation: [12000, 8500, 15000, 9300, 17800, 14200]
Grown copy: [12000, 8500, 15000, 9300, 17800, 14200, 0, 0, 0]
Q1 revenue: [12000, 8500, 15000]
Q2 revenue: [9300, 17800, 14200]
Cloned: [12000, 8500, 15000, 9300, 17800, 14200]
fill, equals, deepEquals and deepToString for 2D Arrays
Three methods that get overlooked but solve daily problems immediately.
Arrays.fill() initialises every slot of an array to a given value. This is perfect for building a game board with default values, resetting a cache, or pre-populating a configuration array. It's O(n) and far cleaner than a manual loop.
Arrays.equals() compares two arrays element-by-element and returns true if they have the same length and identical values at every position. The critical thing to know: == on arrays compares references, not content. This trips up beginners constantly.
For 2D arrays (arrays of arrays), Arrays.equals() isn't enough — it only looks at the outer array's references. You need Arrays.deepEquals() to compare nested arrays properly, and Arrays.deepToString() to print them readably. If you've ever printed a 2D array and got [[I@6d06d69c style garbage, deepToString is the fix.
import java.util.Arrays; public class FillEqualsDeepDemo { public static void main(String[] args) { // --- Arrays.fill() --- // Setting up a 5-seat cinema row, all seats available (true = free) boolean[] cinemaRow = new boolean[5]; Arrays.fill(cinemaRow, true); // mark every seat as available System.out.println("Cinema row: " + Arrays.toString(cinemaRow)); // Book seat index 2 — mark as taken cinemaRow[2] = false; System.out.println("After booking seat 2: " + Arrays.toString(cinemaRow)); // --- Arrays.equals() vs == --- int[] scoreSetA = {10, 20, 30, 40}; int[] scoreSetB = {10, 20, 30, 40}; // == checks if they are the SAME object in memory — almost always false System.out.println("scoreSetA == scoreSetB: " + (scoreSetA == scoreSetB)); // Arrays.equals checks actual content — this is what you want System.out.println("Arrays.equals: " + Arrays.equals(scoreSetA, scoreSetB)); // --- 2D Arrays: deepToString and deepEquals --- // A 3x3 tic-tac-toe board char[][] boardA = { {'X', 'O', 'X'}, {'O', 'X', 'O'}, {'O', 'X', 'X'} }; char[][] boardB = { {'X', 'O', 'X'}, {'O', 'X', 'O'}, {'O', 'X', 'X'} }; // Arrays.toString on a 2D array gives garbage — use deepToString System.out.println("2D with toString: " + Arrays.toString(boardA)); System.out.println("2D with deepToString:" + Arrays.deepToString(boardA)); // Arrays.equals on 2D arrays compares only the outer references System.out.println("Arrays.equals on 2D: " + Arrays.equals(boardA, boardB)); // Arrays.deepEquals dives into nested arrays — this is correct System.out.println("Arrays.deepEquals on 2D: " + Arrays.deepEquals(boardA, boardB)); } }
After booking seat 2: [true, true, false, true, true]
scoreSetA == scoreSetB: false
Arrays.equals: true
2D with toString: [[[C@6d06d69c, [[C@7852e922, [[C@4e25154f]
2D with deepToString:[[X, O, X], [O, X, O], [O, X, X]]
Arrays.equals on 2D: false
Arrays.deepEquals on 2D: true
Sorting Objects with a Custom Comparator
Sorting integers is straightforward, but real applications sort objects — products by price, employees by name, orders by date. Arrays.sort() has an overload that accepts a Comparator, giving you complete control over the sort order without changing your data class.
This is where understanding the contract matters. A Comparator must return a negative number if the first argument should come before the second, zero if they're equal, and a positive number if the first should come after. The Comparator.comparing() factory method and method references make this clean and readable.
You can also chain comparators using .thenComparing() for multi-level sorting — sort by department first, then by salary within each department. This pattern mirrors SQL's ORDER BY col1, col2 and is incredibly useful in reporting and analytics code.
import java.util.Arrays; import java.util.Comparator; public class SortingObjectsWithComparator { // A simple Product record to represent real-world data record Product(String name, String category, double price) { @Override public String toString() { return String.format("%-18s | %-12s | $%.2f", name, category, price); } } public static void main(String[] args) { Product[] inventory = { new Product("Wireless Mouse", "Electronics", 29.99), new Product("USB-C Hub", "Electronics", 49.99), new Product("Desk Lamp", "Furniture", 34.99), new Product("Standing Desk", "Furniture", 399.00), new Product("Keyboard", "Electronics", 79.99), new Product("Notebook Pack", "Stationery", 12.49) }; // --- Sort by price ascending (cheapest first) --- // Comparator.comparingDouble extracts a double key for comparison Arrays.sort(inventory, Comparator.comparingDouble(Product::price)); System.out.println("=== By Price (Ascending) ==="); for (Product p : inventory) System.out.println(p); // --- Sort by price descending --- // .reversed() flips the order without writing extra logic Arrays.sort(inventory, Comparator.comparingDouble(Product::price).reversed()); System.out.println("\n=== By Price (Descending) ==="); for (Product p : inventory) System.out.println(p); // --- Multi-level sort: category first, then price within category --- // thenComparingDouble is only consulted when category values are equal Arrays.sort(inventory, Comparator.comparing(Product::category) .thenComparingDouble(Product::price) ); System.out.println("\n=== By Category, then Price ==="); for (Product p : inventory) System.out.println(p); } }
Notebook Pack | Stationery | $12.49
Wireless Mouse | Electronics | $29.99
Desk Lamp | Furniture | $34.99
USB-C Hub | Electronics | $49.99
Keyboard | Electronics | $79.99
Standing Desk | Furniture | $399.00
=== By Price (Descending) ===
Standing Desk | Furniture | $399.00
Keyboard | Electronics | $79.99
USB-C Hub | Electronics | $49.99
Desk Lamp | Furniture | $34.99
Wireless Mouse | Electronics | $29.99
Notebook Pack | Stationery | $12.49
=== By Category, then Price ===
Wireless Mouse | Electronics | $29.99
USB-C Hub | Electronics | $49.99
Keyboard | Electronics | $79.99
Desk Lamp | Furniture | $34.99
Standing Desk | Furniture | $399.00
Notebook Pack | Stationery | $12.49
| Method | Purpose | Works on 2D? | Modifies Original? | Time Complexity |
|---|---|---|---|---|
| Arrays.sort(arr) | Sort primitive or object array | No (outer only) | YES — in place | O(n log n) |
| Arrays.binarySearch(arr, key) | Find element index in sorted array | No | No | O(log n) |
| Arrays.copyOf(arr, len) | Copy full or resized array | No (outer only) | No | O(n) |
| Arrays.copyOfRange(arr, f, t) | Copy a sub-section of an array | No | No | O(n) |
| Arrays.fill(arr, val) | Set every element to a value | No (outer only) | YES — in place | O(n) |
| Arrays.equals(a, b) | Compare two 1D arrays by content | NO — use deepEquals | No | O(n) |
| Arrays.deepEquals(a, b) | Compare nested (2D/3D) arrays | YES | No | O(n*m) |
| Arrays.toString(arr) | Readable string of a 1D array | NO — use deepToString | No | O(n) |
| Arrays.deepToString(arr) | Readable string of a nested array | YES | No | O(n*m) |
🎯 Key Takeaways
- Arrays.sort() modifies the original array IN PLACE — if you need the original preserved, copy first with Arrays.copyOf()
- Arrays.binarySearch() requires the array to already be sorted — calling it on an unsorted array produces silently wrong results, not an exception
- For 2D arrays, always use deepEquals() for comparison and deepToString() for printing — the non-deep versions only look at the outer array's references
- Array assignment (b = a) is NOT a copy — it's an alias. Both variables point to the same data and mutations through one affect the other
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Using Arrays.binarySearch() on an unsorted array — The method assumes sorted order and uses a binary search algorithm. On an unsorted array it may return wrong indices or fail to find existing elements entirely. Always call Arrays.sort() before Arrays.binarySearch(), or use a different search strategy if you can't sort first.
- ✕Mistake 2: Using == to compare two arrays for equality — This compares memory addresses, not content, so it almost always returns false even when both arrays contain identical values. The symptom is a failed equality check that looks correct to the eye. Fix it by using Arrays.equals() for 1D arrays and Arrays.deepEquals() for 2D arrays.
- ✕Mistake 3: Treating an array assignment as a copy — Writing int[] copy = original just creates a second variable pointing to the same array. Modifying copy[0] silently changes original[0] too, causing subtle data corruption bugs. Use Arrays.copyOf(original, original.length) to get a true independent copy.
Interview Questions on This Topic
- QWhat is the difference between Arrays.equals() and Arrays.deepEquals()? When would using the wrong one cause a bug?
- QArrays.sort() uses different algorithms for primitive arrays versus object arrays. Can you explain why, and what that means for stability?
- QIf you call Arrays.binarySearch() and it returns -4, what does that negative value actually tell you — and why is checking for == -1 dangerous?
Frequently Asked Questions
Does Arrays.sort() return a new sorted array in Java?
No — Arrays.sort() sorts the array IN PLACE and returns void. It modifies the original array directly. If you need to keep the original unsorted, first make a copy with Arrays.copyOf() and then sort the copy.
Why does printing a Java array give something like [I@6d06d69c?
Java arrays don't override toString(), so printing them directly shows the default Object representation — a type code and memory address. Use Arrays.toString(myArray) for 1D arrays and Arrays.deepToString(myArray) for 2D arrays to get a readable output.
Can I use Arrays.sort() to sort in descending order?
For object arrays (like Integer[] or String[]), yes — pass Comparator.reverseOrder() as the second argument: Arrays.sort(arr, Comparator.reverseOrder()). For primitive arrays (like int[]), there's no direct descending sort built in; you either sort ascending and then reverse manually, or convert to Integer[] first to use the Comparator overload.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.