Junior 5 min · March 17, 2026
Arrays Class in Java

Arrays.asList() — Why add() Silently Fails in Production

Arrays.asList() returns a fixed-size inner ArrayList where add() throws UnsupportedOperationException.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.

Follow
Production
production tested
May 24, 2026
last updated
1,554
articles · all by Naren
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Arrays class provides static utility methods: sort (O(n log n)), binarySearch (O(log n)), copyOf, fill, stream, asList, and equals/deepEquals
  • Key components: Sorting (primitives and objects), searching (requires sorted arrays), copying (creates new arrays), filling (initialization), conversion (List views)
  • Performance: sort uses Dual-Pivot Quicksort for primitives (fast), TimSort for objects (stable). copyOf uses System.arraycopy (native O(n))
  • Production trap: Arrays.asList(arr) returns fixed-size list — add/remove throws UnsupportedOperationException, silent runtime crash
  • Biggest mistake: Using binarySearch on unsorted array — returns undefined results (wrong index or negative) with no exception, corrupts downstream logic
✦ Definition~90s read
What is Arrays Class in Java?

Arrays.asList() is a bridge method that wraps an existing array into a List view backed by the original array. It does not create a new ArrayList from java.util.ArrayList — it returns a private inner class called java.util.Arrays.ArrayList, which is a fixed-size list.

Imagine a Swiss Army knife — one tool that does dozens of different jobs.

This is the root cause of the silent UnsupportedOperationException when you call add() or remove(): the backing array cannot be resized, and the inner class inherits those mutators from AbstractList but throws by default. In production, this often manifests as a confusing crash hours after deployment, because the code compiles fine and the list looks like a normal ArrayList at a glance.

The fix is either to wrap the result in new ArrayList<>(Arrays.asList(...)) for a fully mutable list, or to use List.of() (Java 9+) for an immutable list that throws immediately with a clear message. Arrays.asList() also preserves reference semantics — modifying an element through the list modifies the original array, which can cause subtle concurrency bugs if the array is shared across threads without synchronization. For sorting, Arrays.sort() works directly on primitive arrays with tuned dual-pivot quicksort (O(n log n)), while Collections.sort() or List.sort() operate on the wrapper.

Searching with Arrays.binarySearch() requires a sorted array and returns -(insertion point) - 1 on miss, a convention that trips up junior devs. Copying via Arrays.copyOf() and System.arraycopy() are the fastest ways to clone or resize arrays, used internally by ArrayList for its growth strategy. Arrays.stream() bridges arrays into the Streams API, enabling parallel operations on primitives without boxing overhead — critical for high-throughput data pipelines.

When you don't need mutability or stream operations, Arrays.asList() is fine for quick iteration or passing to legacy APIs expecting a List; otherwise, prefer List.of() for immutability or new ArrayList<>() for full mutability.

Plain-English First

Imagine a Swiss Army knife — one tool that does dozens of different jobs. The Arrays class is exactly that for Java arrays: it can sort them, search them, copy them, fill them with default values, compare them, and even turn them into modern stream pipelines. Instead of writing the same array-handling logic by hand for the hundredth time, you import java.util.Arrays and let the standard library do the heavy lifting.

Java arrays are low-level. They know their length and hold values. That's about it. Need to sort them? Write your own Quicksort. Need to search? Write a loop. Need to compare two arrays for equality? Loop through every element. The Arrays utility class exists because writing this boilerplate over and over is a waste of your time and a source of subtle bugs.

Arrays is one of the most-used classes in the Java standard library. It's not glamorous — no one brags about knowing Arrays — but every senior developer reaches for it constantly. It contains the stable, battle-tested implementations of algorithms you'd otherwise have to reimplement. Knowing which method does what, and more importantly, what each method does NOT do, separates efficient code from code that crashes at runtime.

By the end you'll know how to sort, search, copy, fill, and compare arrays using the standard library. You'll also know the gotchas: why asList returns an unmodifiable list, why binarySearch returns a negative insertion point, and why deepEquals exists for multi-dimensional arrays.

Arrays.asList() — The Fixed-Size List That Looks Like an ArrayList

Arrays.asList() is a bridge between Java arrays and the Collection framework. It wraps an existing array into a List implementation backed by that array — no copying, no resizing. The returned object is a fixed-size list from java.util.Arrays' private inner class ArrayList (not java.util.ArrayList). This means you cannot add() or remove() elements; any attempt throws UnsupportedOperationException at runtime. The list is serializable and implements RandomAccess for O(1) index operations. Changes to the list write through to the original array and vice versa. Use Arrays.asList() when you need a List view of an array for legacy APIs, iteration, or passing to methods that accept Collection. Never use it when you need a dynamically growable list — that's new ArrayList<>(Arrays.asList(...)) or List.of() in Java 9+. In production, this distinction causes silent failures when code assumes add() works, leading to unexpected exceptions in data pipelines or batch processing.

Not an ArrayList
Arrays.asList() returns a fixed-size list backed by the original array — calling add() throws UnsupportedOperationException, not a compile-time error.
Production Insight
A team used Arrays.asList() to build a list of user IDs for batch processing, then called add() to append new IDs — the batch job crashed with UnsupportedOperationException after hours of processing.
The exception surfaces only at runtime when add() is invoked, often in rarely-tested code paths like error handling or edge-case data flows.
Rule: If you need add() or remove(), wrap the result in new ArrayList<>(Arrays.asList(...)) or use List.of() for immutable lists.
Key Takeaway
Arrays.asList() returns a fixed-size list backed by the original array — add() and remove() are unsupported.
The returned list is not java.util.ArrayList; it's a private inner class that delegates to the array.
Always wrap with new ArrayList<>() if you need a resizable list, or use List.of() for immutability.
Arrays.asList() Fixed-Size List Trap THECODEFORGE.IO Arrays.asList() Fixed-Size List Trap Why add() silently fails and how to avoid it Arrays.asList() Returns fixed-size list backed by array Fixed-Size List Cannot add or remove elements add() Call Throws UnsupportedOperationException Production Failure Silent bug if uncaught Fix: new ArrayList<>() Wrap in mutable list constructor ⚠ add() on Arrays.asList() throws UnsupportedOperationException Always wrap in new ArrayList<>(Arrays.asList(...)) for mutability THECODEFORGE.IO
thecodeforge.io
Arrays.asList() Fixed-Size List Trap
Arrays Class Java

Sorting Arrays

Arrays.sort() is the workhorse of the utility class. For primitive arrays (int[], double[], char[]), it uses Dual-Pivot Quicksort — an O(n log n) algorithm that's faster than traditional Quicksort on most real-world data. For object arrays (String[], Integer[], custom objects), it uses TimSort, a hybrid of merge sort and insertion sort that's stable (preserves order of equal elements) and performs well on partially-sorted data.

You can sort the entire array or a specific range: Arrays.sort(numbers, fromIndex, toIndex). The toIndex is exclusive, so sort(arr, 1, 4) sorts indices 1, 2, 3 only.

Sorting objects requires the class to implement Comparable (natural order) or you provide a Comparator. The Comparator version is powerful: Arrays.sort(people, Comparator.comparing(Person::getAge).reversed()) sorts by age descending using method references.

For parallel processing of large arrays (>8K elements), Arrays.parallelSort() uses the Fork/Join framework to split the array across CPU cores. It's significantly faster on multi-core systems for arrays > 1M elements.

io/thecodeforge/java/arrays/ArraysSortDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.thecodeforge.java.arrays;

import java.util.Arrays;

public class ArraysSortDemo {
    public static void main(String[] args) {
        int[] numbers = {5, 2, 8, 1, 9, 3};
        Arrays.sort(numbers);
        System.out.println(Arrays.toString(numbers)); // [1, 2, 3, 5, 8, 9]

        // Sort a range (from index 1 to 4, exclusive end)
        int[] partial = {5, 2, 8, 1, 9, 3};
        Arrays.sort(partial, 1, 4);  // sorts indices 1,2,3 only
        System.out.println(Arrays.toString(partial)); // [5, 1, 2, 8, 9, 3]

        // Sort objects — natural order (Comparable)
        String[] words = {"banana", "apple", "cherry", "date"};
        Arrays.sort(words);
        System.out.println(Arrays.toString(words));   // [apple, banana, cherry, date]

        // Sort objects — custom Comparator (by length)
        Arrays.sort(words, (a, b) -> a.length() - b.length());
        System.out.println(Arrays.toString(words));   // [date, apple, banana, cherry]
    }
}
Pro Tip: Use parallelSort for Large Arrays in Multi-Core Environments
Arrays.parallelSort(arr) automatically splits the array into chunks and sorts them in parallel using multiple CPU cores. For arrays larger than ~8,192 elements, it's measurably faster. For small arrays, the overhead makes it slower than regular sort. Always benchmark for your data size.
Production Insight
Arrays.sort on objects requires them to be Comparable or you must provide a Comparator. Missing comparator throws ClassCastException at runtime.
TimSort detects malformed comparators that violate consistency rules (compare(a,b) must be opposite of compare(b,a)) and throws IllegalArgumentException.
Rule: For custom sorting, always use Comparator.comparing with method references — it's less error-prone than manual comparators.
Key Takeaway
Arrays.sort() uses Dual-Pivot Quicksort for primitives and TimSort for objects — both O(n log n) and production-tested.
parallelSort() splits work across CPU cores for large arrays (>1M elements).
Rule: Use Comparator.comparing for custom sorting. It's clearer and less error-prone than manual lambda comparators.
Sorting Strategy Selection
IfArray size < 10,000, primitives
UseUse Arrays.sort(arr). Dual-Pivot Quicksort is optimal. No need for parallelSort overhead.
IfArray size > 100,000, multiple CPU cores available, primitives or objects
UseUse Arrays.parallelSort(arr). Fork/Join parallel sort is 2-4x faster on large arrays. Test with your data size.
IfArray of objects with natural order (implements Comparable)
UseArrays.sort(arr). Objects (String, Integer, LocalDate) have natural order. No comparator needed.
IfArray of objects with custom ordering (by field, length, etc.)
UseArrays.sort(arr, Comparator.comparing(Obj::getField)). Use method references. For multiple fields, chain: .thenComparing(...)
IfArray contains null elements
UseUse Comparator.nullsFirst(Comparator.naturalOrder()) or nullsLast. Never pass null to comparator without handling.

Searching, Copying, and Filling

Arrays.binarySearch() performs an O(log n) search on a sorted array. It returns the index of the element if found, otherwise a negative value: -(insertion point) - 1. This negative value tells you where the element would be inserted to keep the array sorted. The array MUST be sorted before calling binarySearch — otherwise the result is undefined.

Arrays.copyOf() creates a new array copy. It takes the original array and a new length. If the new length is larger than the original, extra elements are filled with default values (0 for int, false for boolean, null for objects). If smaller, the array is truncated.

Arrays.fill() sets all elements (or a range) to a specific value. It's useful for initializing arrays to a known state before use. For large arrays, fill is efficient because it operates on the underlying memory directly.

Arrays.equals() compares two arrays element by element. The arrays must be the same length and all elements must be equal (using .equals() for objects). For multi-dimensional arrays, use deepEquals() which recursively compares nested arrays.

io/thecodeforge/java/arrays/ArraysUtilities.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package io.thecodeforge.java.arrays;

import java.util.Arrays;

public class ArraysUtilities {
    public static void main(String[] args) {
        int[] sorted = {1, 3, 5, 7, 9, 11};

        // binarySearch — MUST be sorted first
        System.out.println(Arrays.binarySearch(sorted, 7));  // 3 (index)
        System.out.println(Arrays.binarySearch(sorted, 4));  // negative = not found

        // copyOf — new array, truncates or pads with zeros
        int[] copy = Arrays.copyOf(sorted, 4);
        System.out.println(Arrays.toString(copy));   // [1, 3, 5, 7]

        int[] extended = Arrays.copyOf(sorted, 8);
        System.out.println(Arrays.toString(extended)); // [1, 3, 5, 7, 9, 11, 0, 0]

        // copyOfRange — copy a slice
        int[] slice = Arrays.copyOfRange(sorted, 2, 5);
        System.out.println(Arrays.toString(slice));  // [5, 7, 9]

        // fill — initialise all elements
        int[] grid = new int[5];
        Arrays.fill(grid, -1);
        System.out.println(Arrays.toString(grid));   // [-1, -1, -1, -1, -1]

        // equals and deepEquals
        int[] a = {1, 2, 3};
        int[] b = {1, 2, 3};
        System.out.println(a == b);           // false — different references
        System.out.println(Arrays.equals(a, b)); // true  — element comparison
    }
}
Watch Out: binarySearch on Unsorted Array Returns Undefined
If the array is not sorted, binarySearch may return a negative number (wrong) or even a positive index (also wrong). It does NOT throw an exception. The result is undefined. Always sort first: Arrays.sort(arr); int idx = Arrays.binarySearch(arr, target);
Production Insight
The insertion point formula -(insertion point) - 1 is consistent across JVMs. To recover insertion point: int insertionPoint = -(result + 1);
binarySearch on an unsorted array is a silent bug — no exception, just wrong results. It's one of the most misdiagnosed Array issues.
Rule: If you use binarySearch, you must be absolutely certain the array is sorted. Sort it in the same method, or pass a guaranteed sorted array from a validated source.
Key Takeaway
binarySearch requires a sorted array — always sort first or guarantee input is sorted.
The return negative value encodes the insertion point: -(insertion point) - 1.
Rule: Use copyOf() to create array copies. Use fill() to initialize. Use equals() for element comparison, not ==, which compares references.
binarySearch Return Value Interpretation
IfbinarySearch returns value >= 0
UseElement found at that index. Safe to use as index directly. The array was sorted at search time.
IfbinarySearch returns negative value
UseElement not found. Insertion point = -(returnValue + 1). Check if array was sorted — an unsorted array can also return negative. Validate sortedness.
IfbinarySearch returned negative but element exists
UseArray is unsorted. Sort it first: Arrays.sort(arr); then search. Also check custom Comparator if using binarySearch(arr, target, comparator).
IfNeed to insert element while maintaining sorted order
Useint idx = Arrays.binarySearch(arr, newVal); if (idx < 0) { idx = -idx - 1; } // then insert at idx (array shift required)
IfArray contains duplicates
UsebinarySearch returns index of any matching element (not necessarily the first). For first occurrence, linear search from that index back to start.

Arrays.stream and Arrays.asList

Arrays.stream() turns an array into a Stream for functional operations. For primitive arrays (int[], double[], long[]), it returns a specialized stream (IntStream, DoubleStream, LongStream) that provides efficient operations like sum(), average(), and boxed(). For object arrays, it returns a regular Stream<T>.

The stream is sequential by default. For parallel processing, use .parallel() on the stream: Arrays.stream(arr).parallel().map(...).toArray().

Arrays.asList() is the most misunderstood method in the class. It returns a fixed-size List view of the array — NOT a mutable ArrayList. Changes to the array are reflected in the list, and vice versa. But you cannot add or remove elements — those operations throw UnsupportedOperationException.

To get a mutable list, wrap it: new ArrayList<>(Arrays.asList(arr)). For Java 9+, List.of(arr) returns an immutable list; wrapping with new ArrayList<>(List.of(arr)) gives a mutable copy.

io/thecodeforge/java/arrays/ArraysStreamDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.thecodeforge.java.arrays;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

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

        // Arrays.stream — IntStream for primitives
        int sum = Arrays.stream(numbers).sum();
        System.out.println("Sum: " + sum);  // 55

        double avg = Arrays.stream(numbers).average().orElse(0);
        System.out.println("Avg: " + avg);  // 5.5

        int[] evens = Arrays.stream(numbers)
                           .filter(n -> n % 2 == 0)
                           .toArray();
        System.out.println(Arrays.toString(evens));  // [2, 4, 6, 8, 10]

        // Arrays.asList — fixed-size List view (add/remove throw UnsupportedOperationException)
        String[] arr = {"a", "b", "c"};
        List<String> list = Arrays.asList(arr);
        list.set(0, "x");  // set works — modifies underlying array
        System.out.println(Arrays.toString(arr));  // [x, b, c]
        // list.add("d");  // throws UnsupportedOperationException

        // For a mutable list, wrap: new ArrayList<>(Arrays.asList(arr))
    }
}
Interview Gold: Why asList Returns a Fixed-Size List
Arrays.asList is a bridge from array to Collection. It returns a view of the array, not a copy. The list cannot grow or shrink because the underlying array is fixed-size. This is by design — but it surprises junior engineers constantly. The correct pattern for a mutable list is new ArrayList<>(Arrays.asList(arr)).
Production Insight
Arrays.asList returns an inner class ArrayList (not java.util.ArrayList). Its class name misleadingly starts with 'ArrayList' but it's not resizable.
The list.set(index, value) works because it writes back to the underlying array. This is useful for batch updates.
Rule: Use Arrays.asList only when you need a read-only view of an array. For any modification beyond set(), wrap in a new ArrayList.
Key Takeaway
Arrays.stream() bridges arrays to functional pipelines; use boxed() for primitive arrays to get Stream<Integer>.
Arrays.asList() returns a fixed-size list — add/remove throw UnsupportedOperationException. Wrap in new ArrayList<>() for mutability.
Rule: For primitive streams, IntStream/LongStream/DoubleStream methods (sum, average) are more efficient than boxing to objects.
Converting Array to List
IfNeed read-only view, no modifications, occasional set() only
UseUse Arrays.asList(arr). Memory efficient (no copy). List is backed by original array; changes to array reflect in list.
IfNeed mutable list (add, remove, etc.)
UseUse new ArrayList<>(Arrays.asList(arr)). Creates a copy. Memory overhead but fully resizable.
IfArray elements will not change after conversion, need immutable list
UseJava 9+: List.copyOf(arr). Immutable, thread-safe, no modification allowed. Java 8: Collections.unmodifiableList(Arrays.asList(arr))
IfPrimitive array (int[]) to List<Integer>
UseArrays.stream(primitiveArr).boxed().collect(Collectors.toList()). Arrays.asList does NOT box primitives.
IfNeed to convert list back to array
Uselist.toArray(new String[0]); (for String list). For primitive lists, use stream().mapToInt().toArray().

Why Arrays Is a Final Utility Class You Can't Extend

You don't instantiate java.util.Arrays. It's a final class with a private constructor. All methods are static. This is deliberate: arrays are low-level primitives in the JVM, not objects you polymorph. The class exists solely to give you safe, optimized operations without reinventing sorting or binary search. Ever seen someone new Arrays() in production? That's a code smell from someone who skipped the docs. Use Arrays.sort() directly. The JVM inlines these calls aggressively. You get performance and readability. Plus, since it's final, you can't override equals() or hashCode() and break the contract. That's saved me from debugging a LOB application where a dev tried to subclass Arrays for logging. Don't. Just don't.

ArraysFinalCheck.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// io.thecodeforge
import java.util.Arrays;

public class ArraysFinalCheck {
    // Attempting to instantiate or subclass Arrays
    // This won't compile — comment out to test
    // public class MyArrays extends Arrays { }

    public static void main(String[] args) {
        // This is how you're meant to use it:
        int[] scores = {88, 72, 95, 61, 84};
        Arrays.sort(scores);
        System.out.println("Sorted scores: " + Arrays.toString(scores));
        /* Output:
           Sorted scores: [61, 72, 84, 88, 95]
        */
    }
}
Output
Sorted scores: [61, 72, 84, 88, 95]
Production Trap:
Trying to extend Arrays or create an instance will fail at compile time. Don't fight the design. Use static imports for cleaner code.
Key Takeaway
Arrays is a final utility class. Never instantiate it. Call its static methods directly.

binarySearch() — The Silent Bug That Eats Production Data

binarySearch() looks simple. You pass a sorted array and a key. It returns the index or a negative insertion point. But here's where juniors burn production: the array must be sorted first. If you search an unsorted array, the result is undefined — it can return wrong indices or negative values you misinterpret. I inherited a ticket where a payment batch system was double-processing invoices. Root cause? Dev called binarySearch on a list that wasn't sorted after a concurrent modification. The returned negative value was taken as 'not found', then the code inserted a duplicate. Always sort before search. Use the overloaded version with fromIndex and toIndex if you're working on subranges. And remember: the comparator version is for custom objects. If your Comparator is inconsistent with equals, expect chaos.

BinarySearchSafe.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// io.thecodeforge
import java.util.Arrays;

public class BinarySearchSafe {
    public static void main(String[] args) {
        int[] invoiceIds = {304, 102, 589, 221, 456};
        
        // WRONG: search without sort
        // int result = Arrays.binarySearch(invoiceIds, 221);
        
        // RIGHT: sort first
        Arrays.sort(invoiceIds);
        int key = 221;
        int index = Arrays.binarySearch(invoiceIds, key);
        
        if (index >= 0) {
            System.out.println("Found invoice " + key + " at index " + index);
            // Output: Found invoice 221 at index 2
        } else {
            System.out.println("Insert at: " + (-index - 1));
        }
    }
}
Output
Found invoice 221 at index 2
Production Trap:
binarySearch on an unsorted array returns garbage. Always sort before search in the same thread — or use a concurrent-safe copy.
Key Takeaway
Sort first. Then search. Trust the negative return for insertion points, but never apply it to raw indices.
● Production incidentPOST-MORTEMseverity: high

The Unmodifiable List That Crashed the Upsell Engine

Symptom
The upsell engine loaded a product category list from configuration, then attempted recommendations.add(newRecommendation). No exception appeared in logs. The list size remained 3. Customers saw the same recommendations every time. A senior engineer finally added a try-catch and discovered UnsupportedOperationException was being thrown but caught and swallowed by a generic exception handler.
Assumption
The team assumed any List returned by Arrays.asList() was a normal ArrayList. They didn't know it returns a fixed-size list backed by the original array — you cannot add or remove elements. The code worked in development because the list never needed to grow. In production, new product recommendations triggered the add operation, causing the crash.
Root cause
List<String> categories = Arrays.asList(configCategories); came from a configuration array. The code then called categories.add(newCategory). Arrays.asList returns an inner class ArrayList (not java.util.ArrayList), which is a fixed-size view. add() and remove() are not supported and throw UnsupportedOperationException. A generic catch-all exception handler logged ERROR but didn't include the stack trace (misconfigured logger), so the team saw a log line but not the exception type. The program continued, but the list never grew.
Fix
1. Changed List<String> categories = new ArrayList<>(Arrays.asList(configCategories)); — wrap the fixed-size list in a mutable ArrayList. 2. For Java 9+, used List.of(...) for immutable lists (explicitly documented as immutable) and new ArrayList<>(List.of(...)) for mutable copies. 3. Added exception logging that prints the full stack trace for UnsupportedOperationException. 4. Added unit test that attempts to add to every list created from Arrays.asList, verifying the wrapped version works. 5. Replaced all Arrays.asList().add() patterns in codebase with proper mutable wrapper.
Key lesson
  • Arrays.asList() returns a fixed-size list. You cannot add() or remove() elements. The list is backed by the original array.
  • The exception thrown (UnsupportedOperationException) is a runtime exception. Your code compiles and runs — until add() is called.
  • Always wrap Arrays.asList() result in new ArrayList<>() if you need a mutable list: new ArrayList<>(Arrays.asList(arr))
  • For truly immutable lists in Java 9+, use List.of(...). At least its behavior is documented and throws an explicit exception.
  • Never swallow exceptions with a bare catch (Exception e) {}. Always log the stack trace. The team lost a week because the logger omitted the exception type.
Production debug guideSymptom → Action mapping for common Arrays class failures in production Java applications.5 entries
Symptom · 01
UnsupportedOperationException when calling add() or remove() on a list
Fix
The list came from Arrays.asList() or List.of(). Arrays.asList returns a fixed-size list backed by array. Wrap in mutable list: new ArrayList<>(originalList).
Symptom · 02
binarySearch returns negative number but element exists in array
Fix
Array is unsorted. binarySearch requires the array to be sorted in ascending order. Sort first: Arrays.sort(arr); int idx = Arrays.binarySearch(arr, target);
Symptom · 03
Arrays.equals returns false for arrays with identical content
Fix
You are comparing arrays with ==, not Arrays.equals(). array1 == array2 compares object references, not element values. Use Arrays.equals(array1, array2) for 1D, Arrays.deepEquals(array1, array2) for 2D.
Symptom · 04
Modifying array reflects in list returned by Arrays.asList — unexpected behavior
Fix
Arrays.asList() returns a view backed by the original array. Changes to the array affect the list, and vice versa. If you want a snapshot copy, use new ArrayList<>(Arrays.asList(arr)).
Symptom · 05
NullPointerException in Comparator when sorting with custom comparator
Fix
The array contains null elements, and the comparator does not handle nulls. Use Comparator.nullsFirst() or Comparator.nullsLast(): Arrays.sort(arr, Comparator.nullsLast(String::compareTo));
★ Java Arrays Class Debug Cheat SheetFast diagnostics for Arrays class issues in production Java applications.
UnsupportedOperationException when modifying list from Arrays.asList
Immediate action
Check if list was created from Arrays.asList()
Commands
grep -n 'Arrays.asList' src/ | grep -v 'new ArrayList'
echo 'list.getClass().getName()' → java.util.Arrays$ArrayList
Fix now
Wrap with ArrayList: new ArrayList<>(Arrays.asList(arr)). For Java 8, use mutable copy. For Java 10+, use List.copyOf() for immutable copy.
binarySearch returns negative but element exists+
Immediate action
Check if array is sorted before binarySearch
Commands
grep -B5 'Arrays.binarySearch' src/ | grep 'Arrays.sort'
echo 'boolean sorted = true; for (int i = 1; i < arr.length; i++) if (arr[i-1] > arr[i]) sorted = false;'
Fix now
Add Arrays.sort(arr); before binarySearch. Ensure array is sorted consistently (same Comparator if using binarySearch with Comparator).
Equals returns false for identical arrays+
Immediate action
Check if using == instead of Arrays.equals
Commands
grep -n 'arr1 == arr2' src/
grep -n 'Arrays.equals' src/
Fix now
Replace if (arr1 == arr2) with if (Arrays.equals(arr1, arr2)). For multi-dimensional arrays, use Arrays.deepEquals(arr1, arr2).
copyOf not copying full array — missing last elements+
Immediate action
Check new length parameter: copyOf(arr, newLength) where newLength is larger than arr.length?
Commands
grep -n 'Arrays.copyOf' src/ | grep '\, [0-9]\+'
echo 'Arrays.copyOf(arr, arr.length + extraSpace)'
Fix now
If newLength > arr.length, extra elements are filled with default values (0 for int, false for boolean, null for objects). For exact copy, use newLength = arr.length. For truncation, newLength < arr.length works.
Stream from array returns unexpected results (sum, average wrong)+
Immediate action
Check if using IntStream for int[] or Stream<Integer> for Integer[]
Commands
grep -n 'Arrays.stream' src/ | grep -E '\[\]'
echo 'For int[], Arrays.stream(arr).sum() works; for Integer[], .mapToInt(Integer::intValue)'
Fix now
For primitive arrays (int[]), Arrays.stream(arr) returns IntStream. For object arrays (Integer[]), returns Stream<Integer>. Use .mapToInt(Integer::intValue) to convert.
Arrays Class Utility Methods
MethodReturnsSide Effect on Original?Time ComplexityPrecondition
sort(arr)voidYes (sorts in-place)O(n log n)Elements must be Comparable (for objects) or primitive
binarySearch(arr, key)int (index or negative insertion point)No — reads onlyO(log n)Array must be sorted ascending
copyOf(arr, newLength)new arrayNo — creates copyO(newLength)None
fill(arr, value)voidYes (fills entire array)O(n)None
asList(arr)List<T> (fixed-size viewChanges to list affect array)O(1) (view creation)Array of objects (not primitives)
stream(arr)Stream/IntStreamNoO(1) (view creation)None
equals(arr1, arr2)booleanNoO(n)Arrays must be same length? Not required — returns false if lengths differ
deepEquals(arr1, arr2)booleanNoO(n) recursivelyFor multi-dimensional arrays

Key takeaways

1
Arrays.sort() uses Dual-Pivot Quicksort for primitives and TimSort for objects
both O(n log n) and production-tested.
2
binarySearch requires a sorted array
always sort first or guarantee input is sorted; an unsorted array leads to undefined results.
3
Arrays.asList() returns a fixed-size list backed by the array
add/remove throw UnsupportedOperationException. Wrap with new ArrayList<>(...) for mutability.
4
Arrays.equals() compares element values; == compares references. Use deepEquals() for multi-dimensional arrays.
5
copyOf() creates a new array; fill() initializes arrays; stream() enables functional pipelines.

Common mistakes to avoid

5 patterns
×

Using Arrays.asList(arr) and calling add() or remove()

Symptom
UnsupportedOperationException at runtime. The code compiles fine, so it's only discovered when the code path is executed in production.
Fix
Wrap Arrays.asList result in a mutable ArrayList: new ArrayList<>(Arrays.asList(arr)). For Java 9+, use List.of(...) if immutable is desired, but still wrap if you need add/remove.
×

Calling binarySearch on an unsorted array

Symptom
binarySearch returns a negative number (incorrect, element not found) or even a positive index (also incorrect if element exists but at wrong position). No exception, just wrong results that can corrupt downstream logic.
Fix
Always call Arrays.sort(arr) before Arrays.binarySearch(arr, target). Or ensure the array is guaranteed sorted via external process. For debugging, add assertion: assert isSorted(arr);
×

Using == to compare arrays instead of Arrays.equals()

Symptom
Equality check always returns false even when arrays contain identical elements. == compares object references, not element values.
Fix
Use Arrays.equals(arr1, arr2) for 1D arrays, or Arrays.deepEquals(arr1, arr2) for multi-dimensional arrays.
×

Assuming Arrays.asList() works on primitive arrays

Symptom
Writing List<int> list = Arrays.asList(primitiveArray) does not compile. Or List<Integer> list = Arrays.asList(primitiveArray) compiles but produces a single-element list containing the array object, not the elements.
Fix
For primitive to List conversion: List<Integer> list = Arrays.stream(primitiveArray).boxed().collect(Collectors.toList());
×

Modifying array after Arrays.asList() and expecting list not to change

Symptom
The list reflects changes to the original array. Two variables (array and list) unexpectedly stay in sync, causing confusion about where the data came from.
Fix
If you need a snapshot, create a copy: List<String> list = new ArrayList<>(Arrays.asList(arr));. Now changes to the array will not affect the list.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the difference between Arrays.equals() and == for arrays?
Q02SENIOR
Why does Arrays.asList() not support the add() method?
Q03SENIOR
What does Arrays.binarySearch() return when the element is not in the ar...
Q04JUNIOR
How do you convert an int[] to List in Java?
Q01 of 04JUNIOR

What is the difference between Arrays.equals() and == for arrays?

ANSWER
== compares the object references of the array variables. Two arrays with identical contents but different objects in memory will return false with ==. Arrays.equals() compares the element values: length must be equal and each pair of elements must be equal (using Objects.equals() for objects, == for primitives). For multi-dimensional arrays, Arrays.equals() does not recursively compare nested arrays — it uses shallow comparison. For deep comparison (nested arrays), use Arrays.deepEquals().
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
Why does Arrays.asList() not support add() or remove()?
02
What does binarySearch() return when the element is not found?
03
What is the difference between Arrays.sort() and Arrays.parallelSort()?
04
How do I copy a range of an array using Arrays class?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Notes here come from systems that actually shipped.

Follow
Verified
production tested
May 24, 2026
last updated
1,554
articles · all by Naren
🔥

That's Arrays. Mark it forged?

5 min read · try the examples if you haven't

Previous
Array Searching in Java
5 / 8 · Arrays
Next
Jagged Arrays in Java