Сортировка массива Java по своим правилам методом `Arrays.sort()`
Например, имеется массив int
: {0, -14, 191, 161, 19, 144, 195, 1}
.
Хочется, используя метод Arrays.sort()
отсортировать массив по абсолютному значению, возможно ли это сделать с помощью компаратора?
Ответы (2 шт):
Сортировка массивов при помощи компаратора НЕ доступна для массивов примитивных типов int[]
, long[]
, double[]
и т.д., поэтому для кастомной сортировки придётся преобразовать массив либо в список List<Integer>
либо в массив Integer[]
, отсортировать его при помощи метода Arrays.sort(T[] arr, Comparator<? super T>) c
, и затем преобразовать обратно в массив примитивов.
При этом вместо компаратора следует передавать функцию для конвертации Integer
в примитив, на основании которой можно построить компаратор при помощи Comparator.comparingInt(ToIntFunction<? super T>) fn
.
public static void sortIntArray(int[] arr, ToIntFunction<Integer> c) {
Integer[] ints = new Integer[arr.length];
Arrays.setAll(ints, i -> arr[i]);
Arrays.sort(ints, Comparator.comparingInt(c));
Arrays.setAll(arr, i -> ints[i]);
}
Также можно использовать Stream API и возвращать новый отсортированный массив, однако следует учесть, что IntStream
также не поддерживает кастомную сортировку, и придется преобразовать его в Stream<Integer>
и затем обратно при помощи Stream::mapToInt
:
public static int[] sortInts(int[] arr, ToIntFunction<Integer> c) {
return Arrays.stream(arr) // IntStream
.boxed() // Stream<Integer>
.sorted(Comparator.comparingInt(c)) // Stream<Integer>
.mapToInt(Integer::intValue) // IntStream
.toArray(); // int[]
}
Тест:
int[] arr = {0, -14, 191, 161, 19, 144, 195, 1};
sortIntArray(arr, Math::abs);
// или arr = sortInts(arr, Math::abs);
System.out.println(Arrays.toString(arr));
Вывод:
[0, 1, -14, 19, 144, 161, 191, 195]
Аналогично, в метод для кастомной сортировки можно передавать IntUnaryOperator
, принимающий и отдающий результат типа int
, тогда при вызове компаратора нужно будет передать ссылку на соответствующий метод IntUnaryOperator::applyAsInt
:
public static int[] sortInts(IntUnaryOperator fun, int... arr) {
return Arrays.stream(arr)
.boxed()
.sorted(Comparator.comparingInt(fun::applyAsInt))
// аналогия с явным анбоксингом
// .sorted(Comparator.comparingInt(i -> fun.applyAsInt(i.intValue())))
.mapToInt(Integer::intValue)
.toArray();
}
Есть в Java такое скользкое место: нельзя сортировать массивы примитивов используя компаратор. Способы, которые предлагает стандартная библиотека, предполагают два копирования содержимого массива. Копируете данные в массив или коллекцию объектов, сортируете, копируете обратно. Хочется решить задачу без копирований.
Такой способ есть: создадим наследника AbstractList
, который будет выглядеть как список Integer
, но данные будет хранить в массиве. Если такого наследника отсортировать, упорядоченным окажется массив в котором хранятся данные.
import java.util.Arrays;
import java.util.AbstractList;
import java.util.Comparator;
public class Temp {
public static void main(String[] args) {
int[] array = {0, -14, 191, 161, 19, 144, 195, 1};
System.out.println(Arrays.toString(array));
sortArray(array, Comparator.comparingInt(Math::abs));
System.out.println(Arrays.toString(array));
}
public static void sortArray(int[] array, Comparator<? super Integer> c) {
new IntList(array).sort(c);
}
private static class IntList extends AbstractList<Integer> {
private final int[] array;
public IntList(int[] array) { this.array = array; }
@Override
public Integer get(int i) { return array[i]; }
@Override
public int size() { return array.length; }
@Override
public Integer set(int i, Integer v) {
int old = array[i];
array[i] = v;
return old;
}
}
}
Всё работает:
$ javac Temp.java && java Temp [0, -14, 191, 161, 19, 144, 195, 1] [0, 1, -14, 19, 144, 161, 191, 195]
Если вы не боитесь анонимных классов, код можно записать короче:
import java.util.Arrays;
import java.util.AbstractList;
import java.util.Comparator;
public class Temp {
public static void main(String[] args) {
int[] array = {0, -14, 191, 161, 19, 144, 195, 1};
System.out.println(Arrays.toString(array));
sortArray(array, Comparator.comparingInt(Math::abs));
System.out.println(Arrays.toString(array));
}
public static void sortArray(int[] array, Comparator<? super Integer> c) {
new AbstractList<Integer>() {
@Override
public Integer get(int i) { return array[i]; }
@Override
public int size() { return array.length; }
@Override
public Integer set(int i, Integer v) {
int old = array[i];
array[i] = v;
return old;
}
}.sort(c);
}
}