1. Introduction
Sorting an array is a common operation in Java, but sometimes, we also need to know the original indices of the elements after sorting. This information can be crucial for certain algorithms and applications.
In this tutorial, we’ll illustrate different approaches to achieve this in Java.
2. Problem Description
Sorting an array is a fundamental operation, but in some scenarios, it’s not just about arranging values in order; we also need to retain the original positions of these values. This becomes especially crucial when we want to know how the order of elements changes after sorting. Let’s consider the following array:
int[] array = {40, 10, 20, 30};
Before sorting, the indices (positions) of elements in this array are:
- Index 0: 40
- Index 1: 10
- Index 2: 20
- Index 3: 30
After sorting this array, we obtain the new indices of the elements:
- Index 0: 10 (originally at index 1)
- Index 1: 20 (originally at index 2)
- Index 2: 30 (originally at index 3)
- Index 3: 40 (originally at index 0)
We aim to sort this array in ascending order while also tracking how the indices change based on the sorted values.
3. Using a Custom Comparator with Indices
One approach to obtaining the indices after sorting is to use a custom comparator that maintains the index alongside the array elements. Furthermore, this method allows us to sort the array based on element values while keeping track of their original positions.
Now, let’s demonstrate this with some code:
int[] array = {40, 10, 20, 30};
@Test
void givenArray_whenUsingCustomComparator_thenSortedIndicesMatchExpected() {
Integer[] indices = new Integer[array.length];
for (int i = 0; i < array.length; i++) {
indices[i] = i;
}
Arrays.sort(indices, Comparator.comparingInt(i -> array[i]));
assertArrayEquals(new Integer[]{1, 2, 3, 0}, indices);
}
In this example, we initialize an indices array to hold the indices of the original array array. Each element of the indices array represents the index of the corresponding element in the array.
By using a custom comparator with the Arrays.sort() method, we specify that the indices array should be sorted based on the values in the array. Moreover, the Comparator.comparingInt(i -> array[i]) compares elements in the indices array based on the values of the array at those indices.
After sorting, we use assertArrayEquals() to verify that the sorted indices array matches the expected order [1, 2, 3, 0].
4. Using Java 8 Stream API
One of the significant new features in Java 8 was the introduction of the stream functionality – java.util.stream – which contains classes for processing sequences of elements.
Here’s how we can leverage the Java 8 Stream API to obtain and sort indices based on the values in the original array:
@Test
void givenArray_whenUsingStreamAPI_thenSortedIndicesMatchExpected() {
List<Integer> indices = IntStream.range(0, array.length)
.boxed().sorted(Comparator.comparingInt(i -> array[i])).collect(Collectors.toList());
assertIterableEquals(Arrays.asList(1, 2, 3, 0), indices);
}
Here, we utilize the IntStream.range() method to generate a stream of integer indices from 0 to array.length – 1. This stream is then boxed into a Stream
Then, we use the sorted() operation, using a comparator defined by Comparator.comparingInt(i -> array[i]). Here, each index i in the stream is mapped to the corresponding value in the array, and the comparator is based on these values. Finally, we use the collect(Collectors.toList()) method to collect the sorted indices into a List
5. Conclusion
In conclusion, we explored effective methods in Java for sorting arrays while preserving the original elements indices. This information is crucial for various algorithms and applications where maintaining the positional relationship of elements is important.
As usual, the accompanying source code can be found over on GitHub.