Arrays.sort() rearranges array elements in ascending order (numeric or alphabetical). It modifies the original array directly — no new array is created
Key components: import java.util.Arrays; call Arrays.sort(array); for descending, use Integer[] with Collections.reverseOrder()
Performance: Dual-Pivot Quicksort for primitives (O(n log n)), TimSort for objects — both are stable sorts (maintain relative order of equal elements)
Production trap: Trying to reverse an int[] with Collections.reverseOrder() — compile error because reverseOrder() only works on object arrays
Biggest mistake: Forgetting that Arrays.sort() sorts in-place — you lose the original order permanently unless you made a copy first
✦ Definition~90s read
What is Array Sorting in Java?
Array sorting in Java is the process of arranging elements in a defined order—typically ascending or descending—using the java.util.Arrays class. The primary method is Arrays.sort(), which for primitive arrays uses a dual-pivot Quicksort algorithm (O(n log n) average time, O(log n) space) and for object arrays uses TimSort, a hybrid stable sort derived from merge sort and insertion sort (O(n log n) worst-case, O(n) space).
★
Imagine you have a messy stack of exam papers, each with a score written on top.
Sorting is not a one-time operation because Java arrays are mutable; any subsequent modification—like appending elements via Arrays.copyOf() or manual insertion—invalidates the sorted order, breaking algorithms like binary search that rely on a consistent, sorted state. This is a common pitfall: developers sort once, then add items, and wonder why Arrays.binarySearch() returns garbage results or negative insertion points.
In the Java ecosystem, sorting is a prerequisite for efficient search, but it's not always the right tool. For small arrays (under ~50 elements), linear search often outperforms binary search due to overhead. For dynamic collections, prefer ArrayList with Collections.sort() (which also uses TimSort) over raw arrays, as it handles resizing and maintains order more naturally.
Custom sorting is achieved via Comparator—for example, sorting strings by length instead of alphabetically—which you pass to Arrays.sort(T[], Comparator). The choice between Arrays.sort() and Collections.sort() hinges on data structure: use Arrays.sort() for arrays (primitive or object), and Collections.sort() for List implementations like ArrayList or LinkedList.
Both delegate to the same underlying TimSort for objects, but Arrays.sort() on primitives uses the faster, non-stable dual-pivot Quicksort. Real-world numbers: sorting 10 million integers with Arrays.sort() takes ~1-2 seconds on modern hardware; TimSort on objects is about 20-30% slower due to comparison overhead and stability guarantees.
Plain-English First
Imagine you have a messy stack of exam papers, each with a score written on top. Before you can hand them back in order from lowest to highest, you need to sort them. That's exactly what array sorting does — it takes a jumbled list of values stored in your program and rearranges them into a predictable order. Java has a built-in helper called Arrays.sort() that does this sorting for you automatically, the same way a tidy assistant would sort those papers without you lifting a finger.
Every real application deals with ordered data. A leaderboard ranks players by score. A contacts list shows names alphabetically. A shop sorts products by price. None of that is possible if your data is sitting in random order. Sorting is one of the most fundamental operations in programming, and the sooner you get comfortable with it, the faster you'll be able to build things that actually feel polished and useful.
Before sorting existed as a built-in feature, developers had to write dozens of lines of code manually swapping values around — a slow, error-prone nightmare. Java's Arrays utility class solves this entirely. It ships with a sort() method powered by a highly optimised algorithm under the hood, so you get fast, reliable sorting in a single line of code without needing to understand the internals.
By the end you'll know how to sort primitive arrays in ascending order, reverse them into descending order, sort arrays of strings alphabetically, write a custom sort rule using a Comparator, and avoid the classic traps that trip up beginners. You'll walk away with working code you can drop straight into a real project.
Why Array Sorting in Java Is Not a One-Time Operation
Array sorting in Java rearranges elements into a defined order — ascending by default via Arrays.sort(), which uses Dual-Pivot Quicksort for primitives (O(n log n) average) and TimSort for objects (O(n log n) worst-case). The core mechanic is in-place mutation: the original array reference is unchanged, but its contents are permuted. This is critical because any code holding that reference now sees sorted data, which can break assumptions made before the sort.
A sorted array enables O(log n) binary search via Arrays.binarySearch(), but only if the array remains sorted. Appending a single element after sorting invalidates the precondition — binary search will return arbitrary negative values or miss the element entirely. The array is not a self-maintaining structure; it is a static snapshot. Developers often forget that Arrays.sort() does not lock the array or prevent future modifications.
Use sorting when you need fast lookups on a static dataset — configuration loaded at startup, reference tables, or read-heavy caches. Never sort an array that will later grow or shrink without re-sorting. For dynamic collections, prefer TreeSet or PriorityQueue which maintain order on insertion. In production, a single unsorted append after sort can silently corrupt search results, leading to data loss or incorrect business logic.
Sorted ≠ Immutable
A sorted array is a contract, not a property. Any mutation after sort breaks binary search — no exception is thrown, just wrong results.
Production Insight
A payment service loaded a sorted list of valid merchant IDs at startup, then appended new IDs during runtime without re-sorting. Binary search returned false negatives, causing valid transactions to be rejected silently.
Symptom: Intermittent 'merchant not found' errors that disappeared after a restart — the array was re-sorted on boot, masking the bug.
Rule: If you sort an array, treat it as read-only. For any append, either re-sort or switch to a self-balancing structure like TreeSet.
Key Takeaway
Sorting an array mutates it in place — all references see the new order.
Binary search on a sorted array is O(log n) but only correct if the array has not been modified since sorting.
For dynamic data, use a sorted collection (TreeSet, PriorityQueue) instead of sorting an array repeatedly.
thecodeforge.io
Array Sorting in Java — Appended Items Break Binary Search
Array Sorting Java
Sorting a Basic Number Array With Arrays.sort()
Before you can sort anything, you need to import Java's Arrays class. It lives in the java.util package and it's the toolbox that contains sort() and many other helpful array utilities. Think of it like importing a specialised calculator — once it's imported, all its functions are available to you.
Arrays.sort() takes your array as an argument and sorts it in-place. 'In-place' means it rearranges the values inside the original array — it doesn't create a new one. After the method runs, your original variable now holds the sorted data. This is important to keep in mind because there's no 'undo' — the original order is gone.
For arrays of primitive numbers (int, double, long etc.), the default sort is always ascending — smallest value first, largest value last. This mirrors natural numeric order, the same way you'd read numbers on a number line from left to right.
The algorithm Java uses internally for primitive arrays is called Dual-Pivot Quicksort. You don't need to understand it yet, but know that it's extremely fast — even an array of a million numbers sorts in a fraction of a second.
package io.thecodeforge.sorting;
import java.util.Arrays; // Gives us access to the Arrays utility classpublicclassSortStudentScores {
publicstaticvoidmain(String[] args) {
// A class of 6 students just got their test results back — completely out of orderint[] studentScores = { 78, 45, 92, 61, 33, 88 };
// Print the scores BEFORE sorting so we can see the differenceSystem.out.println("Before sorting: " + Arrays.toString(studentScores));
// Arrays.sort() rearranges the values inside studentScores from low to high// No new array is created — the same array is modified directlyArrays.sort(studentScores);
// Print the scores AFTER sorting — now lowest to highestSystem.out.println("After sorting: " + Arrays.toString(studentScores));
// We can now easily find the lowest score (index 0) and highest (last index)int lowestScore = studentScores[0];
int highestScore = studentScores[studentScores.length - 1];
System.out.println("Lowest score: " + lowestScore);
System.out.println("Highest score: " + highestScore);
}
}
Pro Tip:
Always use Arrays.toString() when printing an array — printing the array variable directly (e.g. System.out.println(studentScores)) gives you a memory address like [I@6d06d69c, which is useless for debugging.
Production Insight
Arrays.sort() sorts in-place — the original array is permanently changed. No copy is made, no new array is returned.
If you need the original order for any future operation, you must copy before sorting: int[] copy = original.clone(); Arrays.sort(copy);
Rule: Never sort an array that other parts of your code still rely on in its original unsorted order. Copy first unless you're certain.
Key Takeaway
Arrays.sort() sorts in-place — the original array is permanently changed, so copy it first if you need the original order preserved.
Reverse order requires switching from int[] to Integer[] — primitives don't support Comparators, object wrappers do.
Rule: Always use Arrays.toString() when printing arrays — the raw variable prints a useless memory address, not the values.
IfArray is large (>10M elements) and you need to sort repeatedly
→
UseUse Arrays.parallelSort(arr) for parallel sorting using multiple CPU cores. Faster on large arrays, slower on small ones.
Sorting Strings Alphabetically and Numbers in Descending Order
Sorting strings works exactly the same way as sorting numbers — just call Arrays.sort() and Java handles the alphabetical ordering for you. Under the hood, Java compares strings character by character using their Unicode values, which means uppercase letters ('A' = 65) sort before lowercase letters ('a' = 97). For everyday words that share the same case, this just looks like normal A-to-Z ordering.
Now, what about descending order — highest to lowest? This is where things get slightly different. Arrays.sort() has no built-in 'reverse' flag for primitive arrays. The cleanest workaround for int[] is to sort ascending first, then reverse the array manually. Alternatively, you can switch from a primitive int[] to an Integer[] (the object wrapper version), which unlocks a second form of Arrays.sort() that accepts a Comparator — a rule that tells Java how to compare two items.
Comparators.reverseOrder() is a ready-made comparator that flips the sort direction. Think of it as telling Java: 'for every comparison you make, do the opposite of what you'd normally do.'
This section shows both approaches side by side so you can choose whichever fits your situation.
package io.thecodeforge.sorting;
import java.util.Arrays;
import java.util.Collections; // Needed for Collections.reverseOrder()publicclassSortNamesAndRankings {
publicstaticvoidmain(String[] args) {
// ── PART 1: Sort a String array alphabetically ──────────────────────String[] playerNames = { "Zara", "Ahmed", "Priya", "Luca", "Mei" };
System.out.println("Names before sort: " + Arrays.toString(playerNames));
Arrays.sort(playerNames); // Sorts A-to-Z using Unicode/alphabetical orderSystem.out.println("Names after sort: " + Arrays.toString(playerNames));
// ── PART 2: Sort integers in DESCENDING order (highest first) ────────// We must use Integer[] (capital I — the object wrapper) NOT int[]// because Collections.reverseOrder() only works with objects, not primitivesInteger[] highScores = { 1500, 3200, 800, 4750, 2100 };
System.out.println("\nScores before sort: " + Arrays.toString(highScores));
// Pass Collections.reverseOrder() as the second argument to flip the sortArrays.sort(highScores, Collections.reverseOrder());
System.out.println("Scores after sort: " + Arrays.toString(highScores));
// The top player's score is now conveniently sitting at index 0System.out.println("Top score belongs to: " + playerNames[0] + " with " + highScores[0]);
}
}
Watch Out:
Collections.reverseOrder() does NOT work with int[] — it causes a compile error. You must use Integer[] (the wrapper class) instead. This is one of the most common beginner mistakes when trying to sort in reverse order.
Production Insight
int[] and Integer[] are not interchangeable in sorting. int[] uses Dual-Pivot Quicksort (primitive operations). Integer[] uses TimSort with Comparator.
For descending order, converting int[] to Integer[] and back is expensive: O(n) boxing + O(n) sorting + O(n) unboxing.
Rule: For large primitive arrays (>1M ints), prefer sorting ascending then manual reversal in-place (O(n) reverse) over boxing to Integer[] (which duplicates memory).
Key Takeaway
Reverse order requires switching from int[] to Integer[] — primitives don't support Comparators, object wrappers do.
For large arrays, ascending sort + manual reversal is faster than boxing/unboxing to Integer[].
Rule: Use Collections.reverseOrder() on object arrays only (Integer[], String[], etc.). For int[], reverse manually after ascending sort.
Descending Sort Implementation
IfArray size < 100,000, convenience over performance
→
UseBox int[] to Integer[], sort with reverseOrder(), unbox back: int[] sorted = Arrays.stream(arr).boxed().sorted(Collections.reverseOrder()).mapToInt(i->i).toArray();
IfArray size > 100,000, performance matters
→
UseSort ascending with Arrays.sort(arr), then reverse in-place: for (int i=0; i<arr.length/2; i++) { int t = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = t; }
IfYou already have Integer[] array
→
UseUse Arrays.sort(arr, Collections.reverseOrder()). No boxing overhead. This is the cleanest approach.
IfYou need to sort by custom comparator descending
→
UseUse Arrays.sort(arr, (a,b) -> comparator.compare(b, a)) where comparator is your custom ascending comparator. Or use comparator.reversed().
IfArray contains primitives and you sort descending frequently
→
UseStore as Integer[] from the start if possible. The memory overhead (8 bytes per element extra for reference + object header) is acceptable for moderate sizes (<10M).
Custom Sorting Rules With a Comparator (Sort by Word Length, Not Alphabet)
Sometimes alphabetical or numeric order isn't what you need. What if you want to sort a list of movie titles by how long the title is — shortest first? Or sort products by the number of characters in their name? This is where a custom Comparator comes in.
A Comparator is simply a rule that tells Arrays.sort() how to decide which of two elements should come first. You hand it two items — call them 'a' and 'b' — and your rule returns a negative number if 'a' should come first, a positive number if 'b' should come first, or zero if they're equal. Java uses this rule for every comparison it makes during the sort.
In modern Java (version 8 and above) you can write a Comparator as a lambda expression — a short, inline piece of logic that looks like an arrow: (a, b) -> someRule. This means you don't need to write a whole separate class just to define one comparison rule.
Once you understand this pattern, you can sort absolutely anything by any rule you can imagine — it's one of the most powerful and flexible tools in Java.
package io.thecodeforge.sorting;
import java.util.Arrays;
publicclassSortMovieTitlesByLength {
publicstaticvoidmain(String[] args) {
String[] movieTitles = {
"Inception",
"Up",
"The Dark Knight",
"Dune",
"Interstellar"
};
System.out.println("Titles before sort: " + Arrays.toString(movieTitles));
// Custom Comparator using a lambda expression:// (a, b) -> Integer.compare(a.length(), b.length())//// For each pair of titles, compare their lengths:// - If a is shorter than b → negative number → a goes first// - If a is longer than b → positive number → b goes first// - If equal length → 0 → keep current orderArrays.sort(movieTitles, (a, b) -> Integer.compare(a.length(), b.length()));
System.out.println("Titles sorted by length (short → long):");
// Loop through and print each title with its character countfor (String title : movieTitles) {
System.out.println(" " + title + " (" + title.length() + " chars)");
}
// BONUS: Reverse the lambda to get longest title first// Simply swap a and b in the comparison: compare(b.length(), a.length())Arrays.sort(movieTitles, (a, b) -> Integer.compare(b.length(), a.length()));
System.out.println("\nTitles sorted by length (long → short):");
for (String title : movieTitles) {
System.out.println(" " + title + " (" + title.length() + " chars)");
}
}
}
Interview Gold:
Interviewers love asking you to 'sort by a custom field'. The pattern is always the same: Arrays.sort(array, (a, b) -> Integer.compare(a.someProperty, b.someProperty)). Memorise this shape and you can adapt it to any sorting problem in seconds.
Production Insight
Custom comparators can be slow if they perform expensive calculations inside the compare method (e.g., database lookups, network calls).
The comparator is called O(n log n) times (20 million times for 1M elements). Each call should be O(1) and cheap.
Rule: Precompute values into a separate array before sorting, then sort indices based on precomputed values. Or use a sorting network if comparator cost is high.
Key Takeaway
Custom Comparators follow one pattern: (a, b) -> Integer.compare(a.field, b.field) — swap a and b to reverse direction.
For multiple fields, chain comparators: Comparator.comparing(Person::getName).thenComparingInt(Person::getAge).
Rule: Never use subtraction for comparison: a.value - b.value can overflow. Always use Integer.compare(a.value, b.value).
Comparator Implementation Choices
IfCompare by single numeric property (age, price, score)
IfCompare by multiple properties (first by name, then by age)
→
UseUse Comparator.comparing(Obj::getName).thenComparingInt(Obj::getAge). This chains comparators cleanly.
IfCompare by string property with case-insensitivity
→
UseUse Comparator.comparing(Obj::getName, String.CASE_INSENSITIVE_ORDER). Or lambda with a.getName().compareToIgnoreCase(b.getName()).
IfCompare by derived property (calculated on the fly)
→
UseIf calculation is cheap (O(1) arithmetic), include in comparator. If expensive, precompute into array of values and use comparator on precomputed.
IfReverse existing comparator
→
UseUse comparator.reversed(). Or in lambda, swap a and b: (a,b) -> original.compare(b, a). Or use Collections.reverseOrder(comparator).
Time and Space Complexity: Dual-Pivot Quicksort vs TimSort
Java uses two different sorting algorithms under the hood depending on whether the array contains primitives or objects. Understanding the complexity of each helps you predict performance for different data sizes and choose the right tool.
Algorithm
Data Type
Average Time
Worst Time
Space
Stable
Dual-Pivot Quicksort
primitives (int[], double[], etc.)
O(n log n)
O(n²)
O(log n)
No
TimSort
objects (Integer[], String[], custom objects)
O(n log n)
O(n log n)
O(n)
Yes
Dual-Pivot Quicksort is in-place (negligible extra memory) and very cache-friendly, making it ideal for primitives. Its worst-case O(n²) occurs only on pathological data (e.g., already sorted with specific pivot choices). Java mitigates this with randomised pivots.
TimSort is a hybrid of merge sort and insertion sort. It's stable (equal elements keep original order) and guarantees O(n log n) worst-case. The O(n) space comes from temporary arrays for merging. Stability is essential for objects because the sort may be a secondary operation after a primary sort (e.g., sort by name, then by age).
Production note: TimSort's space usage can be a concern for huge arrays on memory-constrained systems. If you have a 100 million element Integer[] array, TimSort may require up to 100 million extra references (roughly 800 MB for the temporary array). For primitive arrays, Dual-Pivot Quicksort uses almost no extra memory.
Production Insight
TimSort's O(n) space can cause OutOfMemoryError for very large object arrays (e.g., 100M elements). For such cases, consider sorting with a custom in-place algorithm or using external sorting.
Dual-Pivot Quicksort's O(n²) worst case is rare but possible. If your data has many duplicates or a specific order that triggers worst-case, consider shuffling before sort or using TimSort via Integer[].
Key Takeaway
Primitives → Dual-Pivot Quicksort (fast, in-place, unstable, O(log n) space). Objects → TimSort (stable, guaranteed O(n log n), O(n) space). Choose based on data type and stability requirements.
Algorithm Selection Decision Flow
Collections.sort() vs Arrays.sort() — When to Use Which
Both Collections.sort() and Arrays.sort() use TimSort for object arrays, but they operate on different data structures. Collections.sort() sorts List implementations (like ArrayList, LinkedList), while Arrays.sort() sorts arrays directly.
Feature
Collections.sort()
Arrays.sort()
Input type
List (ArrayList, LinkedList, etc.)
Array (primitive or object)
Algorithm
TimSort for objects
Dual-Pivot Quicksort for primitives, TimSort for objects
In-place
Yes (modifies original list)
Yes (modifies original array)
Stable
Yes
For objects only (TimSort); primitives unstable
Parallel version
None (use list.parallelStream().sorted() )
Arrays.parallelSort()
Syntax
Collections.sort(list)
Arrays.sort(array)
Custom comparator
Collections.sort(list, comparator)
Arrays.sort(array, comparator)
When to use which: - If your data is in an array (e.g., from legacy code or fixed-size buffer), use Arrays.sort(). - If your data is in a List (modern Java prefers collections), use Collections.sort(). - For primitive arrays, Arrays.sort() is the only option. - For large object collections that need stable sorting, both behave identically.
Production trap:Collections.sort() on a LinkedList is very inefficient because TimSort needs O(n log n) random accesses; LinkedList gives O(n) access per element. Always sort ArrayList-backed lists, not LinkedList.
Production Insight
Collections.sort() delegates to List.sort() (since Java 8), which copies the list to an array, sorts, and copies back. This creates a temporary array that doubles memory briefly. For huge lists, consider ArrayList.sort() (same as Collections.sort()) or use Arrays.sort() on the underlying array if accessible.
Key Takeaway
Use Arrays.sort() for arrays, Collections.sort() for Lists. Both use TimSort for objects. Avoid sorting LinkedList — convert to ArrayList first.
Sorting with Stream API: sorted() and sorted(Comparator)
Java 8 introduced the Stream API, which provides a functional way to sort arrays without mutating the original. The stream().sorted() method returns a new stream with elements sorted, leaving the original array untouched. This is ideal for one-off sorting where you don't want to modify the source array.
Stream.sorted() with Comparator: For object arrays, you can pass a Comparator: ``java String[] names = {"Zara", "Ahmed", "Priya"}; String[] sortedDesc = Arrays.stream(names) .sorted(Comparator.reverseOrder()) .toArray(String[]::new); ``
Custom comparator with method references: ``java List<Person> people = ...; List<Person> sorted = people.stream() .sorted(Comparator.comparing(Person::getAge)) .collect(Collectors.toList()); ``
Performance considerations: Stream sorting creates a copy of the data. For large arrays, this doubles memory usage. However, the copy is temporary and will be garbage collected. If you need to sort the original array in-place for performance, use Arrays.sort() instead.
Use stream sorting when you need a sorted version without mutating the original, or when chaining multiple operations (filter, map, sort). Use Arrays.sort() when memory is tight or you need in-place modification for performance.
Production Insight
Stream.sorted() creates an intermediate array for sorting. For arrays of millions of elements, this temporary array doubles memory usage. If you already have the array in memory, Arrays.sort() is more efficient. Stream sorting is best for one-off views or when the data size is moderate.
Key Takeaway
Stream.sorted() returns a new sorted array without modifying the original. Use it for functional pipelines; use Arrays.sort() for in-place, memory-efficient sorting.
Parallel Sorting with Arrays.parallelSort()
Arrays.parallelSort() is a multi-threaded version of sorting that splits the array into chunks, sorts each chunk in parallel using the Fork/Join pool, then merges the results. It is available for both primitive and object arrays.
When to use: - Array size > 10 million elements (threshold varies by system, typically ~8192 for the parallel branch) - Sorting is a bottleneck and you have multiple CPU cores - You are sorting on a non-real-time thread (the Fork/Join pool can delay other tasks)
When NOT to use: - Small arrays (overhead of thread management outweighs benefits) - Real-time or latency-sensitive applications (parallel sorting may block the common Fork/Join pool) - Memory-constrained environments (parallelSort uses additional temporary arrays for merging)
Performance: On a typical 4-core machine, parallelSort can be 2-4x faster for arrays of 10 million integers. The speedup diminishes with more cores due to memory bandwidth limits.
Code example: ``java int[] bigArray = new int[50_000_000]; // fill array... Arrays.parallelSort(bigArray); // sorts using multiple threads ``
Under the hood: The array is divided into subarrays equal to the number of available processors. Each subarray is sorted independently using Dual-Pivot Quicksort (primitives) or TimSort (objects). Then a merge operation combines them.
io/thecodeforge/sorting/ParallelSortDemo.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
package io.thecodeforge.sorting;
import java.util.Arrays;
import java.util.Random;
publicclassParallelSortDemo {
publicstaticvoidmain(String[] args) {
// Create a large arrayint size = 10_000_000;
int[] data = newint[size];
Random rand = newRandom();
for (int i = 0; i < size; i++) {
data[i] = rand.nextInt(1_000_000);
}
// Time regular sortint[] copy1 = data.clone();
long start = System.nanoTime();
Arrays.sort(copy1);
long regularTime = System.nanoTime() - start;
// Time parallel sortint[] copy2 = data.clone();
start = System.nanoTime();
Arrays.parallelSort(copy2);
long parallelTime = System.nanoTime() - start;
System.out.println("Regular sort: " + regularTime / 1_000_000 + " ms");
System.out.println("Parallel sort: " + parallelTime / 1_000_000 + " ms");
System.out.println("Speedup: " + (regularTime / (double) parallelTime) + "x");
}
}
Caveat:
Arrays.parallelSort() uses the common Fork/Join pool, which is shared with parallel streams. Long-running parallel sorts can starve other parallel operations. Consider using a custom pool for critical applications.
Production Insight
In production, profile before adopting parallelSort. The actual speedup depends on array size, data distribution, core count, and memory bandwidth. For arrays smaller than 10 million, Arrays.sort() is often faster due to lower overhead. Also, parallelSort can increase memory pressure because of intermediate merge buffers.
Key Takeaway
Use Arrays.parallelSort() for large arrays (>10M elements) on multi-core systems. For smaller arrays or latency-sensitive contexts, stick with Arrays.sort().
Multi-Key Sorting with Comparator.comparing() and thenComparing()
Sorting by multiple fields is a common requirement: sort employees by department, then by salary within the same department, then by name. Java 8 introduced factory methods in the Comparator interface that make this clean and composable.
Comparator.comparing() extracts a key and returns a comparator that sorts by that key. It's like a lambda that says: "compare two objects by comparing one property of each."
Chaining with thenComparing() adds secondary sort criteria. If two objects are equal by the first comparator, the second one is used, and so on.
Syntax: ```java Comparator<Person> multiComparator = Comparator .comparing(Person::getDepartment) // primary: department name .thenComparingInt(Person::getSalary) // secondary: salary (ascending) .thenComparing(Person::getName); // tertiary: name
Arrays.sort(people, multiComparator); ```
You can reverse any step by calling .reversed() on that comparator. For example, Comparator.comparing(Person::getDepartment).reversed().thenComparingInt(Person::getSalary) sorts by department descending, then salary ascending.
Important: When chaining, be careful with .reversed() placement. To reverse the entire comparator, call .reversed() at the end: multiComparator.reversed().
io/thecodeforge/sorting/MultiKeySortDemo.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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package io.thecodeforge.sorting;
import java.util.Arrays;
import java.util.Comparator;
classEmployee {
String name;
String department;
int salary;
Employee(String name, String department, int salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
@OverridepublicStringtoString() {
returnString.format("%s (%s, $%d)", name, department, salary);
}
}
publicclassMultiKeySortDemo {
publicstaticvoidmain(String[] args) {
Employee[] team = {
newEmployee("Alice", "Engineering", 120000),
newEmployee("Bob", "Engineering", 95000),
newEmployee("Carol", "Sales", 110000),
newEmployee("Dave", "Engineering", 120000),
newEmployee("Eve", "Sales", 95000)
};
System.out.println("Before sorting:");
Arrays.stream(team).forEach(System.out::println);
// Sort by department (ascending), then by salary descending, then by name ascendingComparator<Employee> byDept = Comparator.comparing(Employee::getDepartment);
Comparator<Employee> bySalaryDesc = Comparator.comparingInt(Employee::getSalary).reversed();
Comparator<Employee> byName = Comparator.comparing(Employee::getName);
Comparator<Employee> multi = byDept.thenComparing(bySalaryDesc).thenComparing(byName);
Arrays.sort(team, multi);
System.out.println("\nAfter sorting (dept asc, salary desc, name asc):");
Arrays.stream(team).forEach(System.out::println);
}
}
Method Reference Shortcut:
You can use Comparator.comparing(Person::getName) instead of (a,b) -> a.getName().compareTo(b.getName()). It's shorter and more readable.
Production Insight
Chaining multiple comparators is O(1) per comparison — each comparator is invoked only when the previous ones return 0. For sorting hundreds of thousands of objects with 3-4 keys, performance is negligible. However, if a key extraction is expensive (e.g., file I/O), precompute the key into a field before sorting.
Key Takeaway
Use Comparator.comparing(keyExtractor).thenComparing(anotherKeyExtractor).thenComparing(...) to sort by multiple fields. Apply .reversed() on individual steps or on the entire chain.
Sorting a 2D Array in Java
2D arrays (arrays of arrays) are common in grid-based problems, matrix operations, and data tables. Sorting a 2D array usually means sorting rows based on the values in one or more columns.
Sort by a specific column: Use a custom comparator that compares the elements at the chosen column index.
Example: sort by first column ascending, second column descending: ```java int[][] matrix = { {5, 2}, {1, 9}, {5, 1}, {3, 7} };
Arrays.sort(matrix, (a, b) -> { if (a[0] != b[0]) return Integer.compare(a[0], b[0]); // first column asc else return Integer.compare(b[1], a[1]); // second column desc }); ```
This syntax works because the 2D array is an array of int[] objects. The comparator receives two rows (int[]), and you compare specific indices.
For stable sorting with multiple columns: Use Comparator.comparingInt(row -> row[0]).thenComparingInt(row -> -row[1]) but careful with negation overflow. Better to use the lambda with explicit Integer.compare.
Sorting an array of arrays by multiple fields (like SQL ORDER BY): You can chain comparators using method references if you have a simple class, but for raw 2D arrays, lambdas are clearest.
io/thecodeforge/sorting/Sort2DArray.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
36
37
38
39
package io.thecodeforge.sorting;
import java.util.Arrays;
import java.util.Comparator;
publicclassSort2DArray {
publicstaticvoidmain(String[] args) {
int[][] grid = {
{4, 2, 7},
{1, 9, 3},
{4, 2, 1},
{3, 5, 6}
};
System.out.println("Original:");
print2D(grid);
// Sort by column 0 ascending, then column 1 descending, then column 2 ascendingArrays.sort(grid, (a, b) -> {
if (a[0] != b[0]) returnInteger.compare(a[0], b[0]);
if (a[1] != b[1]) returnInteger.compare(b[1], a[1]);
returnInteger.compare(a[2], b[2]);
});
System.out.println("\nSorted (col0 asc, col1 desc, col2 asc):");
print2D(grid);
// Alternative using Comparator.comparing() with reversed() – note reversed() reverses entire chain// Best for single-column sorts:// Arrays.sort(grid, Comparator.comparingInt(row -> row[0]));
}
privatestaticvoidprint2D(int[][] arr) {
for (int[] row : arr) {
System.out.println(Arrays.toString(row));
}
}
}
Printing 2D Arrays:
Use Arrays.deepToString(grid) to print a 2D array in one line. For readability in debugging, loop and print each row with Arrays.toString().
Production Insight
Sorting 2D arrays with custom lambdas is efficient for moderately sized grids (<100k rows). For huge 2D arrays, consider using a wrapper object that stores the row and implements Comparable, then sort an array of those wrappers. This reduces comparator overhead (row[0] access becomes field access).
Key Takeaway
Sort 2D arrays by passing a lambda comparator to Arrays.sort() that compares specific column indices. Use explicit Integer.compare to handle ties with descending order.
Practice Exercises: Sorting Arrays in Java
Sharpen your sorting skills with these real-world exercises. Each builds on concepts from this guide.
Exercise 1: Sort objects by multiple fields Create a Student class with fields name (String), grade (int), and age (int). Create an array of 10 students with varied data. Sort them by grade descending, then by age ascending, then by name alphabetically. Print the sorted list.
Exercise 2: Sort a 2D array by column Given a 2D array int[][] scores = {{3, 5, 1}, {2, 8, 7}, {3, 5, 3}}; sort the rows by first column ascending, then second column descending, then third column ascending. Verify the result.
Exercise 3: Partial sort using Arrays.sort() Create an int array of 20 random numbers. Sort only the elements from index 5 to index 14 (inclusive) using Arrays.sort(arr, fromIndex, toIndex). Leave the rest untouched. Print the array to verify the partial sort.
Exercise 4: Sort strings by custom rule Given an array of strings, sort them by the number of vowels in each word (case-insensitive). If two words have the same vowel count, sort them by their natural (alphabetical) order. Hint: define a helper method to count vowels.
Exercise 5: Merge sorted arrays Create two sorted int arrays: a = {1, 3, 5, 7} and b = {2, 4, 6, 8}. Merge them into a single sorted array without using Arrays.sort() on the combined array. Use a two-pointer approach. Then verify the result is sorted.
Bonus challenge: Implement a custom sort() method that uses insertion sort for small arrays (size < 20) and delegates to Arrays.sort() for larger ones. Measure the performance difference on 100 random arrays of varying sizes.
Production Insight
Practice exercises like partial sort (range sort) are useful in production when you need to sort only a portion of a larger buffer without disturbing the rest. The Arrays.sort(arr, fromIndex, toIndex) variant is often overlooked but very handy for paginated or windowed data.
Key Takeaway
Practice multi-field sorting, 2D array sorting, and partial sorting to solidify your understanding of comparators and array manipulation.
Sorting a Subarray: Because Your Boss Doesn't Need the Whole Mess
You don't always need to flatten the entire array. Sometimes you only care about a chunk — maybe the first 50 rows of a paginated report, or a sliding window in a telemetry buffer. Arrays.sort() lets you sort a subrange without copying or mutating the rest. The signature is dead simple: Arrays.sort(arr, fromIndex, toIndex). That fromIndex is inclusive, toIndex is exclusive — classic Java half-open range, same as List.subList(). Why does this matter? Because copying an array just to sort part of it burns CPU and memory. Production services at 10k QPS don't have cycles for that. Use subarray sorting when you're processing sorted windows in streaming data or when you need to partially sort results for a UI with virtual scrolling. It's not a general-purpose tool — but when you need it, it's the difference between a clean solution and a hack job.
SubarraySortDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// io.thecodeforge — java tutorialimport java.util.Arrays;
publicclassSubarraySortDemo {
publicstaticvoidmain(String[] args) {
int[] telemetryReadings = {42, 7, 13, 99, 88, 2, 55, 31, 77, 64};
// Sort indices 2 through 6 (inclusive: 2, exclusive: 7)Arrays.sort(telemetryReadings, 2, 7);
// Output: only the [2,7) range is sortedSystem.out.println(Arrays.toString(telemetryReadings));
}
}
Output
[42, 7, 2, 13, 88, 99, 55, 31, 77, 64]
Production Trap:
Using Arrays.sort() on the whole array when you only need a subrange is a common rookie mistake. It sends the full O(n log n) cost and can shift elements you wanted left untouched. Always audit your sort boundaries when working with shared buffers or concurrent read-write patterns.
Key Takeaway
When sorting a subarray, toIndex is exclusive — off-by-one errors here cause silent data corruption in production.
Descending Order: The One Line That Bites Everyone
Java's Arrays.sort() sorts ascending. That's it. No overload for descending. You want descending order on primitives? You code it yourself. For Integer[] or any object array, you pass a Comparator.reverseOrder() — but that returns null on primitives and burns you at compile time. The real move? For small arrays, write a one-liner after sorting: reverse the array in-place with a simple swap loop. For large arrays or hot paths, use Arrays.parallelSort() with a custom comparator for objects, or sort ascending then reverse. The Collections.reverseOrder() trick works only on object wrappers. Boxing a million ints just to sort them descending? That's how you get a 3AM pager from SRE. If you must sort primitives descending, consider using Arrays.stream(yourArray).boxed().sorted(Comparator.reverseOrder()).mapToInt(Integer::intValue).toArray() — but benchmark it first. Stream overhead can slaughter throughput. Know your data size before choosing the weapon.
Need descending order on primitives frequently? Abstract it into a utility: reverse(int[] arr) — one method, no boxing, no streams, no surprises. Your team will thank you when the memory profile stays flat.
Key Takeaway
Arrays.sort() is ascending-only on primitives. Box or reverse manually — your choice, your runtime cost.
Why Array Initialization Is Where Most Devs Waste Memory
You're not just dumping data into a variable. An array in Java is a fixed-size block of memory, and how you create it dictates your production footprint. The 'new' keyword allocates the array object with default values (zeros, nulls) before you touch a single element. That means a 10,000-element int array costs you 40KB the moment you declare it — not when you fill it.
Accessing arrays is O(1) with bracket syntax, but null references in object arrays will crash your pipeline. Always initialize with literal syntax when you know the values at compile time — it's cleaner and avoids the default-initialization step. For dynamic sizes, use new int[n] and loop. There's no dynamic resizing; if you need that, you're looking for ArrayList, but that comes with boxing overhead and a different memory profile. Choose based on what your production loop actually does.
Integer[] and String[] initialize to null, not empty strings or zeros. One unguarded access in a loop and your server goes down. Always check for null after allocation, or use primitive arrays where possible.
Key Takeaway
Know your allocation strategy: literal for known values, 'new' for fixed-size buffers. Defaults are not your friends in object arrays.
Multidimensional Arrays Are Just Jagged Rows — Stop Wiring Them Square
Java doesn't have true 2D arrays. What you get is an array of references to other arrays — each row can be a different length. That's not a bug, it's a feature. A rectangular matrix wastes memory when one dimension is sparse. A jagged array lets you allocate exactly what each row needs, like storing customer orders where order sizes vary.
Access pattern:matrix[row][col] — but remember, each matrix[row] is itself an array. Traversing row-first is cache-friendly because you access contiguous memory within each sub-array. Col-first traversal jumps between sub-array references, thrashing the cache. In production loops with millions of iterations, this is the difference between milliseconds and seconds.
Initialization with double brace syntax looks clever but creates anonymous inner classes — don't do it in production. Use nested loops. And for the love of your team, never use int[][] arr = new int[5][]; without also allocating each row. That's a segfault waiting to happen.
JaggedArrayExample.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 — java tutorialpublicclassJaggedArrayExample {
publicstaticvoidmain(String[] args) {
// Jagged array: each row has different lengthint[][] orders = newint[3][];
orders[0] = newint[]{101, 202};
orders[1] = newint[]{303, 404, 505};
orders[2] = newint[]{606};
// Row-first traversal — cache friendlyfor (int row = 0; row < orders.length; row++) {
for (int col = 0; col < orders[row].length; col++) {
System.out.print(orders[row][col] + " ");
}
System.out.println();
}
// This compiles but throws at runtime// int[][] bad = new int[5][];// System.out.println(bad[0][0]); // NullPointerException
}
}
Output
101 202
303 404 505
606
Senior Shortcut: Row-First Wins
Always iterate rows outside, columns inside. Java stores sub-arrays as separate heap objects; jumping columns then rows kills locality. Your production pipeline will thank you.
Key Takeaway
Multidimensional arrays are jagged by design. Allocate each row explicitly and traverse row-first for cache performance.
Tips and Best Practices
Sorting arrays in Java looks easy, but production code breaks when assumptions fail. First, never sort unmodified arrays in place unless you own the data—use Arrays.copyOf() first to avoid mutating caller's state. Second, Arrays.sort() on primitive arrays uses Dual-Pivot Quicksort, which is O(n log n) average but O(n²) worst-case; if you need guaranteed performance, use Arrays.parallelSort() on large datasets (above ~8K elements) to leverage ForkJoinPool. Third, when sorting objects, TimSort is stable (preserves equal-element order) while Quicksort is not—choose based on whether stability matters. Fourth, prefer Comparator.comparing() chains over anonymous classes for readability. Fifth, avoid sorting null-containing arrays without a null-safe comparator (Comparator.nullsLast()). Finally, measure before optimizing: sorting small arrays with Stream.sorted() adds boxing overhead on primitives—stick with Arrays.sort() for int[], double[], etc.
SortBestPractices.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// io.thecodeforge — java tutorialimport java.util.*;
publicclassSortBestPractices {
publicstaticvoidmain(String[] args) {
String[] names = {"Zoe", "alice", "Bob", null};
// Avoid mutation of originalString[] sorted = Arrays.copyOf(names, names.length);
Arrays.sort(sorted, Comparator.nullsLast(
Comparator.comparing(String::toLowerCase)));
System.out.println("Original: " + Arrays.toString(names));
System.out.println("Sorted: " + Arrays.toString(sorted));
// Use parallelSort for large arraysint[] big = newint[100_000];
Arrays.parallelSort(big); // Faster than sort() on multi-core
}
}
Output
Original: [Zoe, alice, Bob, null]
Sorted: [alice, Bob, Zoe, null]
Production Trap:
Sorting an array passed in from another module silently mutates that data—your fix breaks their invariants. Always copy first unless the contract explicitly allows in-place sorting.
Key Takeaway
Copy before sorting unless you own the data; use null-safe comparators and favor parallelSort above 8K elements.
Learn Java Essentials
Sorting arrays is a core Java skill that forces you to understand four fundamentals. First, mutability: Arrays.sort() modifies the array, Stream.sorted() returns a new one. Choosing wrong corrupts shared state. Second, both Comparable (natural ordering via compareTo()) and Comparator (external ordering) rely on the same contract: consistent with equals, transitive, and throwing NullPointerException on nulls. Third, generics: Arrays.<T>sort() works on object arrays but not primitives—autoboxing helps with List<Integer> but not int[]. Fourth, method references and lambdas: Arrays.sort(arr, (a,b) -> b - a) hides overflow bugs; instead use Comparator.reverseOrder(). These concepts build into streams, optionals, and functional interfaces. Without them, you'll write fragile sorting logic that breaks on edge cases like duplicates, nulls, or large datasets. Master these essentials and sorting becomes a predictable tool, not a guessing game.
Using subtraction-based comparators (like (a,b) -> a - b) overflows for large integers. Always use Integer.compare(a, b) or Comparator.naturalOrder().
Key Takeaway
Three mandatory concepts: mutable vs immutable sort, primitive vs object handling, and overflow-safe comparator construction.
● Production incidentPOST-MORTEMseverity: high
The Sorted Inventory That Weren't Actually Sorted
Symptom
The product listing page sorted by price ascending most of the time, but occasionally products appeared out of order. The out-of-order products were always the newest inventory additions. Refreshing the page sometimes fixed it, sometimes didn't. The array was sorted once at application startup, not before every display.
Assumption
The developer assumed that once you sort an array with Arrays.sort(), it stays sorted forever. They didn't realise that adding new elements to the array (or receiving new data from the database) would break the sorted order. They also didn't know that Arrays.sort() sorts in-place, meaning the original array is permanently modified — but new elements appended after the sort are not automatically inserted correctly.
Root cause
The product list was an array of product objects that was sorted once at application startup. When the inventory system added new products during the day, the code appended them to the end of the array using a naive expansion + assignment. The new elements were not inserted in sorted order. The product listing page continued to use the same array reference, which was now mostly sorted except for the new items appended at the end. Binary search (for quick price lookup) started failing because the array was no longer fully sorted. The page displayed products in the array's order (mostly sorted + new items at end), causing the price-jumping behaviour.
Fix
1. Moved from a raw array to an ArrayList<Product> with automatic sorting before each display using Collections.sort() with a custom comparator.
2. For performance, switched to a TreeSet<Product> with a comparator that sorts by price — the collection maintains sorted order automatically on every insertion (O(log n)).
3. If array was required, implemented insertion sort for new elements: find insertion point using binary search (O(log n)) then shift array elements right (O(n)).
4. Added a metric to measure array sortedness in production: isSorted = true; for (int i = 1; i < arr.length; i++) if (arr[i-1] > arr[i]) isSorted = false;
5. Added a unit test that adds new products after sorting and verifies the array remains sorted.
Key lesson
Arrays.sort() sorts once, not continuously. The array is not magically 'watched' for changes. Any mutation after sort breaks the sorted invariant.
In-place sort means the original array is modified. If you need the original order elsewhere, copy first: int[] copy = Arrays.copyOf(original, original.length); Arrays.sort(copy);
For collections that need to stay sorted after every insert, use TreeSet (unique elements) or PriorityQueue (ordered, allows duplicates) instead of manual sorting.
If an array must remain sorted, never append elements without inserting them in the correct position. Use int idx = Arrays.binarySearch(arr, newValue); if (idx < 0) idx = -idx - 1; then shift and insert.
Production debug guideSymptom → Action mapping for common sorting failures in production Java applications.5 entries
Symptom · 01
Sorted array appears to have items out of order — not fully sorted
→
Fix
The array was sorted once, but new elements were appended without re-sorting. Re-sort: Arrays.sort(arr). Better: use data structure that maintains order (TreeSet, PriorityQueue). Check insert path for binary search to find correct insertion point.
Symptom · 02
Compiler error when using Collections.reverseOrder() on int[]
→
Fix
reverseOrder() works only on object arrays. Change int[] to Integer[]. Or sort ascending then reverse manually: for (int i = 0; i < arr.length/2; i++) { int temp = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = temp; }
Symptom · 03
Binary search returns wrong index on sorted array
→
Fix
The array is not actually sorted (or sorted with different comparator than the search uses). Verify: for (int i = 1; i < arr.length; i++) assert arr[i-1] <= arr[i] : 'Array not sorted at index ' + i;
Symptom · 04
NullPointerException when sorting array with null elements
→
Fix
Comparator.compare() throws NPE on null. Use Comparator.nullsFirst(Comparator.naturalOrder()) or Comparator.nullsLast() to handle nulls gracefully: Arrays.sort(arr, Comparator.nullsLast(String::compareTo));
Symptom · 05
Custom Comparator seems to be looping — sort never finishes for large array
→
Fix
Comparator may violate consistency rules: sign(a,b) must be opposite of sign(b,a). Also must be transitive and reflexive. Violations cause some sorts (TimSort) to throw IllegalArgumentException: 'Comparison method violates its general contract'.
★ Array Sorting Debug Cheat SheetFast diagnostics for sorting issues in production Java applications.
Array not sorted after calling Arrays.sort() — no error−
Immediate action
Check if you're sorting a view or proxy, not the actual array
Ensure the array variable you're sorting is the same reference you're reading later. If you sort a copy, the original remains unsorted. Use Arrays.sort(theActualArray).
Change int[] to Integer[] in variable declaration. Or use manual reversal: sort ascending then swap elements.
Sorting objects with custom Comparator — Exception 'Comparison method violates its general contract'+
Immediate action
Check if comparator is transitive (if a > b and b > c, then a > c) and reflexive
Commands
java -Djava.util.Arrays.useLegacyMergeSort=true // temporary workaround to switch to legacy sort that doesn't check contract
Arrays.sort(testArray, (a,b) -> { int res = a.value - b.value; System.out.println('Comparing ' + a + ' and ' + b + ' -> ' + res); return res; });
Fix now
Rewrite comparator to be consistent. Never return a.value - b.value if values can overflow. Use Integer.compare(a.value, b.value). Ensure compare(a,b) == -compare(b,a).
NullPointerException when sorting array with Comparator+
Immediate action
The array contains null elements, and the comparator doesn't handle null
Arrays.sort(arr, Comparator.nullsFirst(Comparator.naturalOrder())); // or nullsLast
Fix now
Add null handling: if (a == null && b == null) return 0; if (a == null) return -1; if (b == null) return 1; Or use Comparator.nullsFirst().
Array elements changed after sorting — lost original order+
Immediate action
Arrays.sort() sorts in-place. Did you need to preserve the original order?
Commands
int[] originalCopy = Arrays.copyOf(original, original.length); Arrays.sort(originalCopy); // keep original
System.out.println('Original lost: ' + original.equals(originalCopy) + ' after sort?');
Fix now
If you need both sorted and original, copy before sorting: int[] sorted = original.clone(); Arrays.sort(sorted); Use original for original order, sorted for sorted order.
the original array is permanently changed, so copy it first if you need the original order preserved
2
Reverse order requires switching from int[] to Integer[]
primitives don't support Comparators, object wrappers do
3
Custom Comparators follow one pattern
(a, b) -> Integer.compare(a.field, b.field) — swap a and b to reverse direction
4
Always use Arrays.toString() when printing arrays
the raw variable prints a useless memory address, not the values
5
Never use subtraction for comparison (a.value - b.value)
it can overflow. Use Integer.compare(a.value, b.value) instead.
Common mistakes to avoid
5 patterns
×
Using Collections.reverseOrder() on a primitive int[] array
Symptom
Compile-time error: 'no suitable method found for sort(int[], Comparator)' because reverseOrder() returns a Comparator<Object> that doesn't work with primitives.
Fix
Change int[] to Integer[] in variable declaration. Or sort ascending then manually reverse: for (int i=0; i<arr.length/2; i++) { int t = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = t; }
×
Forgetting that Arrays.sort() modifies the original array
Symptom
Later code uses the array, expecting original unsorted order, but finds it sorted. Hard-to-debug because no exception is thrown — just wrong data.
Fix
Make a copy first using int[] copy = Arrays.copyOf(original, original.length); Arrays.sort(copy); The original remains untouched. Use original for original order, copy for sorted order.
×
Printing an array with System.out.println(myArray) and getting a garbage memory address like [I@7852e922
Symptom
Expecting [1, 2, 3], getting cryptic output like [I@6d06d69c. This happens because arrays don't override toString() in Java.
Fix
Always wrap the array in Arrays.toString(myArray) before printing: System.out.println(Arrays.toString(myArray));. For 2D arrays, use Arrays.deepToString(my2DArray).
×
Using subtraction in comparator (a.value - b.value) causing integer overflow
Symptom
Sort order is incorrect for values near Integer.MAX_VALUE/MIN_VALUE. For example, comparing Integer.MIN_VALUE (-2147483648) and 1: MIN_VALUE - 1 = 2147483647 (positive), incorrectly indicating MIN_VALUE > 1.
Fix
Always use Integer.compare(a.value, b.value) or Long.compare(a.value, b.value) which handle overflow correctly. Never use a.value - b.value for comparison.
×
Assuming Comparator with logic that violates the compare contract
Symptom
Comparator may return inconsistent results: compare(a,b) != -compare(b,a). TimSort throws IllegalArgumentException: Comparison method violates its general contract
Fix
Ensure comparator is reflexive (compare(a,a)=0), symmetric (compare(a,b) = -compare(b,a)), and transitive (if a>b and b>c then a>c). Use Comparator helper methods instead of manual logic.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
What sorting algorithm does Arrays.sort() use internally for primitive a...
Q02SENIOR
How would you sort an array of strings by their length in descending ord...
Q03JUNIOR
If I call Arrays.sort() on an int[] and then need the original unsorted ...
Q04SENIOR
How does Java handle sorting of large arrays with parallelSort? When wou...
Q01 of 04SENIOR
What sorting algorithm does Arrays.sort() use internally for primitive arrays vs object arrays, and why are they different?
ANSWER
For primitive arrays (int[], double[], etc.), Arrays.sort() uses Dual-Pivot Quicksort. For object arrays (Integer[], String[]), it uses TimSort (a hybrid of merge sort and insertion sort). Why different? Primitive arrays can use quicksort because it's in-place, faster, and uses less memory — but quicksort is unstable (doesn't preserve order of equal elements). Stable sort isn't required for primitives because equal values are identical. For objects, stability matters: if two objects are equal by the comparator, their original relative order should be preserved. TimSort is stable and guarantees O(n log n) worst-case performance. Quicksort's worst-case O(n²) is unacceptable for objects where comparison is more expensive. The different algorithms optimise for their data type's characteristics.
Q02 of 04SENIOR
How would you sort an array of strings by their length in descending order using a single line of code?
ANSWER
Arrays.sort(stringArray, (a, b) -> Integer.compare(b.length(), a.length())); or Arrays.sort(stringArray, Comparator.comparingInt(String::length).reversed()); The lambda version swaps a and b inside the compare call to reverse direction. The comparator version uses comparingInt to extract length, then .reversed() to flip the order. Both achieve descending sort by length. For production, prefer the Comparator version for readability.
Q03 of 04JUNIOR
If I call Arrays.sort() on an int[] and then need the original unsorted data back, what should I have done before calling sort() — and what class would you use?
ANSWER
You should have made a copy of the original array before sorting, because Arrays.sort() sorts in-place and the original order is lost. Use int[] sortedCopy = original.clone(); or int[] sortedCopy = Arrays.copyOf(original, original.length);. Then sort sortedCopy, leaving original untouched. The Arrays class provides copyOf() for this purpose. For deep copying 2D arrays, use a loop. Never sort the original array if other parts of your code depend on its original order.
Q04 of 04SENIOR
How does Java handle sorting of large arrays with parallelSort? When would you use it instead of regular sort?
ANSWER
Arrays.parallelSort() uses the Fork/Join framework to split the array into chunks, sort each chunk in parallel using multiple CPU cores, then merge the results. It's beneficial for arrays larger than a threshold (~8,192 elements). For very large arrays (millions of elements), parallelSort can be 2-4x faster on multi-core systems. Trade-offs: parallelSort has higher overhead due to task splitting and merging, so it's slower for small arrays. It also uses more memory (temporary buffers). Use parallelSort when: array size > 1 million elements, sorting is a performance bottleneck, you have multiple CPU cores available, and the sorting operation is not on a real-time thread (fork/join pool may block). Use regular sort for small arrays or when single-threaded performance is acceptable.
01
What sorting algorithm does Arrays.sort() use internally for primitive arrays vs object arrays, and why are they different?
SENIOR
02
How would you sort an array of strings by their length in descending order using a single line of code?
SENIOR
03
If I call Arrays.sort() on an int[] and then need the original unsorted data back, what should I have done before calling sort() — and what class would you use?
JUNIOR
04
How does Java handle sorting of large arrays with parallelSort? When would you use it instead of regular sort?
SENIOR
FAQ · 5 QUESTIONS
Frequently Asked Questions
01
How do I sort an array in descending order in Java?
Change your array type from int[] to Integer[] (the object wrapper), then call Arrays.sort(yourArray, Collections.reverseOrder()). You'll need to import both java.util.Arrays and java.util.Collections. This doesn't work on primitive int[] arrays because Comparators only operate on objects.
Was this helpful?
02
Does Arrays.sort() change the original array in Java?
Yes — Arrays.sort() sorts in-place, meaning it rearranges the elements inside the same array you passed in. There's no return value and no new array is created. If you need the original unsorted order preserved, create a copy first using Arrays.copyOf(original, original.length) and sort the copy instead.
Was this helpful?
03
Why does printing a Java array directly show something like [I@6d06d69c instead of the values?
Java arrays don't override the toString() method, so printing them directly shows their internal type identifier and memory address. Wrap the array in Arrays.toString(yourArray) before passing it to System.out.println() and you'll see the actual values formatted as [1, 2, 3] like you'd expect.
Was this helpful?
04
What's the difference between Arrays.sort() and Arrays.parallelSort()?
Arrays.sort() is single-threaded, uses Dual-Pivot Quicksort (primitives) or TimSort (objects). Arrays.parallelSort() splits the array across multiple CPU cores using Fork/Join, merges results. parallelSort is faster for very large arrays (>1M elements) on multi-core systems. Use sort() for small arrays or when sorting is not a bottleneck, parallelSort for large arrays where performance matters.
Was this helpful?
05
How do I sort an array of custom objects by multiple fields (e.g., by name, then by age)?
Use Comparator.comparing(Person::getName).thenComparingInt(Person::getAge). This creates a comparator that first compares by name, and if names are equal, compares by age. For descending, chain .reversed() at the end: Comparator.comparing(Person::getName).reversed().thenComparingInt(Person::getAge). This sorts by name descending, then age ascending.