Если допускается создание дополнительного одномерного массива, в котором собственно и выполняется сортировка, с последующим "переписыванием" входной матрицы отсортированными значениями, это можно сделать следующим образом (при помощи Stream API)
public static void sort(Point[][] arr) {
Point[] sorted = Arrays.stream(arr)
.flatMap(Arrays::stream)
.sorted(Comparator.comparingInt(Point::getX)
.thenComparing(Point::getY)
)
.toArray(Point[]::new);
for (int i = 0, cols = arr[0].length, n = arr.length * cols; i < n; i++) {
arr[i / cols][i % cols] = sorted[i];
}
}
Однако для достаточно больших массивов использование дополнительного массива того же размера может быть нежелательным, и тогда лучше использовать собственную реализацию быстрой сортировки (см. Quicksort Algorithm Implementation in Java) с поправкой на сортировку двумерного массива, т.е. придётся дополнительно определить несколько методов для конвертации одномерного индекса в позицию в матрице и для перестановки элементов в матрице.
public static <T> void quickSort(T arr[][], Comparator<T> cmp) {
quickSort(arr, 0, arr.length * arr[0].length - 1, cmp);
}
public static <T> void quickSort(T arr[][], int begin, int end, Comparator<T> cmp) {
if (begin < end) {
int partitionIndex = partition(arr, begin, end, cmp);
quickSort(arr, begin, partitionIndex - 1, cmp);
quickSort(arr, partitionIndex + 1, end, cmp);
}
}
private static <T> int partition(T arr[][], int begin, int end, Comparator<T> cmp) {
T pivot = getAt(arr, end);
int i = begin - 1;
for (int j = begin; j < end; j++) {
T curr = getAt(arr, j);
if (cmp.compare(curr, pivot) <= 0) {
swap(arr, ++i, j);
}
}
swap(arr, ++i, end);
return i;
}
private static <T> T getAt(T[][] arr, int i) {
return arr[i / arr[0].length][i % arr[0].length];
}
private static <T> void swap(T[][] arr, int i, int j) {
T temp = getAt(arr, i);
int cols = arr[0].length;
arr[i / cols][i % cols] = arr[j / cols][j % cols];
arr[j / cols][j % cols] = temp;
}
Тест для реализации quickSort
(c использованием Lombok):
@Data
@Accessors(fluent = true) // убрать префиксы get в геттерах
@RequiredArgsConstructor
class Point {
private final int x, y;
}
// record Point(int x, int y) {} // аналогично в Java 16+
// public static void main(String ... args) {
Point[][] points = {
{new Point(9, 2), new Point(6, 7), new Point(3, 8), new Point(3, 4)},
{new Point(1, 1), new Point(6, 5), new Point(8, 4), new Point(2, 2)},
{new Point(5, 9), new Point(3, 1), new Point(2, 6), new Point(1, 5)}
};
quickSort(points, Comparator.comparingInt(Point::x).thenComparingInt(Point::y));
for (Point[] p : points) {
System.out.println(Arrays.toString(p));
}
// }
Результат:
[Point(x=1, y=1), Point(x=1, y=5), Point(x=2, y=2), Point(x=2, y=6)]
[Point(x=3, y=1), Point(x=3, y=4), Point(x=3, y=8), Point(x=5, y=9)]
[Point(x=6, y=5), Point(x=6, y=7), Point(x=8, y=4), Point(x=9, y=2)]