Java Sorting Patterns
Sorting techniques in Java
Practical Java sorting examples using streams, comparators, Collections.sort, PriorityQueue for top N, and Map.Entry sorting.
What This Page Is About
In real backend work and interviews, you often need to sort arrays, lists, records, objects, map entries, or keep only the top N items. The important skill is knowing which Java tool fits the situation.
1. Sort an Array of Numbers Using Arrays.stream
If you have a primitive int array, you can stream it, sort it, and collect it back into an array.
import java.util.Arrays;
public class SortNumbersArray {
public static void main(String[] args) {
int[] nums = {5, 2, 9, 1, 3};
int[] sorted = Arrays.stream(nums)
.sorted()
.toArray();
System.out.println(Arrays.toString(sorted));
}
}Output:
[1, 2, 3, 5, 9]2. Sort an Array of Objects Using Arrays.stream
Here we use a Java record with two fields. The array contains products, and we sort by price.
import java.util.Arrays;
import java.util.Comparator;
public class SortObjectArray {
record Product(String name, int price) {}
public static void main(String[] args) {
Product[] products = {
new Product("Keyboard", 75),
new Product("Monitor", 250),
new Product("Mouse", 40)
};
Product[] sortedByPrice = Arrays.stream(products)
.sorted(Comparator.comparingInt(Product::price))
.toArray(Product[]::new);
System.out.println(Arrays.toString(sortedByPrice));
}
}The key idea is that objects need a rule for comparison. Here the rule is: compare products by their price field.
3. Sort a List of Objects by Streaming the List
If you have a List, you can stream it, sort it, and collect the result into a new list. This keeps the original list unchanged.
import java.util.Comparator;
import java.util.List;
public class SortListWithStream {
record Employee(String name, int salary) {}
public static void main(String[] args) {
List<Employee> employees = List.of(
new Employee("Asha", 120_000),
new Employee("Ben", 95_000),
new Employee("Carlos", 140_000)
);
List<Employee> sortedBySalary = employees.stream()
.sorted(Comparator.comparingInt(Employee::salary))
.toList();
System.out.println(sortedBySalary);
}
}This is useful when you want a sorted copy instead of changing the original collection.
4. Sort a List In-Place Using Collections.sort
If the list is mutable and you want to sort the actual list, use Collections.sort or list.sort.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortListInPlace {
record Employee(String name, int salary) {}
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>(List.of(
new Employee("Asha", 120_000),
new Employee("Ben", 95_000),
new Employee("Carlos", 140_000)
));
Collections.sort(
employees,
Comparator.comparingInt(Employee::salary)
);
System.out.println(employees);
}
}The important distinction: streaming creates a sorted result; Collections.sort changes the list itself.
5. Top N Using PriorityQueue
This is one of the most useful patterns. If you only need the top N items, you do not need to fully sort everything.
Example: find the top 3 employees by salary.
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
public class TopNWithPriorityQueue {
record Employee(String name, int salary) {}
public static void main(String[] args) {
List<Employee> employees = List.of(
new Employee("Asha", 120_000),
new Employee("Ben", 95_000),
new Employee("Carlos", 140_000),
new Employee("Dina", 110_000),
new Employee("Evan", 160_000)
);
int n = 3;
PriorityQueue<Employee> minHeap = new PriorityQueue<>(
Comparator.comparingInt(Employee::salary)
);
for (Employee employee : employees) {
minHeap.offer(employee);
// restrict the heap to contain at most n elements
if (minHeap.size() > n) {
minHeap.poll();
}
}
List<Employee> topN = new ArrayList<>(minHeap);
topN.sort(
Comparator.comparingInt(Employee::salary).reversed()
);
System.out.println(topN);
}
}The heap keeps the smallest salary among the current top N at the top. When the heap grows beyond N, we remove that smallest item.
At the end, the heap contains only the top N employees. We sort those N items only for display.
6. Sort a Map by Value Using Map.Entry::getValue
Maps are not normally “sorted by value” directly. The common pattern is to stream the entry set, sort the entries, and collect or print the result.
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class SortMapByValue {
public static void main(String[] args) {
Map<String, Integer> scores = Map.of(
"Asha", 90,
"Ben", 75,
"Carlos", 95,
"Dina", 85
);
Map<String, Integer> sortedByScore = scores.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue,
LinkedHashMap::new
));
System.out.println(sortedByScore);
}
}Output:
{Ben=75, Dina=85, Asha=90, Carlos=95}LinkedHashMap is used so the insertion order of the sorted stream is preserved in the final map.
7. Sort a Map by Value Descending
For descending order, reverse the value comparator.
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class SortMapByValueDescending {
public static void main(String[] args) {
Map<String, Integer> scores = Map.of(
"Asha", 90,
"Ben", 75,
"Carlos", 95,
"Dina", 85
);
Map<String, Integer> sortedByScoreDesc = scores.entrySet()
.stream()
// The explicit generic types are sometimes used to help
// Java infer the comparator's type before calling reversed().
// In many cases they are not required, but they can make
// the compiler's type inference unambiguous.
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue,
LinkedHashMap::new
));
System.out.println(sortedByScoreDesc);
}
}The explicit generic type on Map.Entry can help Java understand the type before calling reversed().
8. Sort Only Part of an Array
Sometimes you do not want to sort the entire array. Java allows you to sort only a specific range.
Sorting a subrange comes up more often than people think:
- sort only page 2 of results
- sort only the active portion of an array
- sort only a window in an algorithm
- sort a partially filled buffer
import java.util.Arrays;
public class SortSubrange {
public static void main(String[] args) {
int[] nums = {9, 4, 8, 2, 7, 1, 6};
Arrays.sort(nums, 1, 5);
System.out.println(Arrays.toString(nums));
}
}Output:
[9, 2, 4, 7, 8, 1, 6]The range is:
Arrays.sort(array, fromIndex, toIndex);where:
- fromIndex is inclusive
- toIndex is exclusive
So:
Arrays.sort(nums, 1, 5);sorts indices:
1, 2, 3, 4while leaving the rest of the array unchanged.
9. Sort Only Part of a List Using subList
Arrays have built-in range sorting support. Lists achieve something similar using subList().
A useful thing to know is that subList() returns a view into the original list, not a copy.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortSubList {
public static void main(String[] args) {
List<Integer> nums =
new ArrayList<>(List.of(
9, 4, 8, 2, 7, 1, 6
));
Collections.sort(nums.subList(1, 5));
System.out.println(nums);
}
}Output:
[9, 2, 4, 7, 8, 1, 6]The call:
nums.subList(1, 5)represents the elements at indices:
1, 2, 3, 4Just like Arrays.sort(...), the start index is inclusive and the end index is exclusive.
How to Think About These Choices
Need a sorted copy?
Use stream().sorted().toList() or Arrays.stream(...).sorted().toArray().
Need to mutate the list?
Use Collections.sort(list, comparator) or list.sort(comparator).
Need only top N?
Use a PriorityQueue. Do not sort everything if you only need a small subset.
Need to sort a map?
Stream the entrySet, sort entries, then collect into LinkedHashMap if order matters.
Common Interview Follow-Ups
Does stream().sorted() modify the original list?
No. It returns a sorted stream. If you collect the result into a list, that is a new sorted list.
When should I use PriorityQueue instead of sorting?
Use PriorityQueue when you only need the top N or bottom N elements. Sorting everything costs more work than maintaining a small heap.
Why use Comparator.comparingInt instead of Comparator.comparing?
comparingInt avoids boxing int values into Integer objects, so it is usually the cleaner choice for primitive int fields.
Why use LinkedHashMap when sorting a map by value?
Because normal HashMap does not preserve insertion order. LinkedHashMap preserves the order produced by the sorted stream.