I’m learning Java 8 streams. Tell me pls, how can I write a sortArray
method more compactly?
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertArrayEquals;
public class TestStream {
/*
* Sort numbers in an array without changing even numbers position
*/
@Test
public void test_1() {
int[] nonSorted = new int[]{3, 4, 5, 2, 1, 6, 9, 8, 7, 0};
int[] expected = new int[]{1, 4, 3, 2, 5, 6, 7, 8, 9, 0};
Integer[] arr = sortArray(nonSorted);
int[] sorted = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
sorted[i] = arr[i];
}
assertArrayEquals(expected, sorted);
}
private Integer[] sortArray(int[] array) {
Map<Integer, Integer> even = extractEven(array);
Integer[] withoutEvens = removeEven(array);
int length = even.size() + withoutEvens.length;
Integer[] result = new Integer[length];
Arrays.sort(withoutEvens);
for (int i = 0; i < withoutEvens.length; i++) {
result[i] = withoutEvens[i];
}
even.forEach((k, v) -> {
System.arraycopy(result, k, result, k + 1, length - k - 1);
result[k] = v;
});
return result;
}
private Map<Integer, Integer> extractEven(int[] array) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) {
map.put(i, array[i]);
}
}
return map;
}
private Integer[] removeEven(int[] array) {
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 != 0) {
list.add(array[i]);
}
}
Integer[] a = new Integer[list.size()];
return list.toArray(a);
}
}
One can think of a solution like:
First we extract the odd integers from the nonSorted[]
and put them on a stack
in sorted fashion.
Why we should use the
stack
in a sorted fashion??The final array needs to be sorted on odd
Integers
basis, the stack follows FIFO(First in Last Out) policy.
Now we take an Instream
and run it from 0
to nonSorted.length-1
and check the original nonSorted
for the odd Integer
; as soon as we find one we replace it with the first element of the stack and pop()
the element from the stack
.
Note: One needs to play around the stack as not every time you will need sorted elements in the stack, but in OP's case this happens to be.
int[] nonSorted = new int[]{3, 4, 5, 2, 1, 6, 9, 8, 7, 0};
LinkedList<Integer> stack = Arrays.stream(nonSorted)
.sorted().filter(s -> s % 2 != 0).boxed()
.collect(Collectors.toCollection(LinkedList::new));
int[] expected = IntStream.rangeClosed(0, nonSorted.length - 1)
.map(s -> nonSorted[s] % 2 != 0 ? stack.pop():nonSorted[s])
.toArray();
I really liked the idea of using a sorted Stack
, but it is not easily parallelizable and got me curious how to solve that.
My idea is sorting indices of uneven elements and depending on the position of the index we can distinguish during the creation of the result array if a number is even or not.
public int[] sortUnevenElements(int[] nonSorted) {
int[] unevenIndices = IntStream.range(0, nonSorted.length).filter(i -> nonSorted[i] % 2 != 0).toArray();
int[] sortedUnevenIndices = Arrays.stream(unevenIndices, 0, unevenIndices.length).boxed()
.sorted(Comparator.comparingInt(i -> nonSorted[i])).mapToInt(Integer::intValue).toArray();
return IntStream.range(0, nonSorted.length).map(i -> {
int idx = Arrays.binarySearch(unevenIndices, i);
return idx >= 0 ? nonSorted[sortedUnevenIndices[idx]] : nonSorted[i];
}).toArray();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With