Не получается вывести матрицу со смещением строк
Есть метод, в котором формируется матрица с заданной размерностью.
Она должна выводится в виде:
1 2 3 4 5 6
2 3 4 5 6 1
3 4 5 6 1 2
4 5 6 1 2 3
5 6 1 2 3 4
6 1 2 3 4 5
Код:
public class MatrixVer2 {
public static int[][] Matrix2(int lenght) {
int[][] arr = new int[lenght][lenght];
for (int i = 0; i < arr.length; i++) {
int count = 1 ;
for (int j = i + 1; j < arr[i].length; j++) {
arr[i][j] = count++;
//System.out.print(j + " \t");
if (count > arr.length) {
count = 1;
}
}
//System.out.println("\n");
}
for (int[] res: arr) {
System.out.println("\n");
for (int x: res) {
System.out.print(x + " \t");
}
}
return arr;
}
public static void main(String[] args) {
Matrix2(5);
}
}
У меня получается следующий вид:
0 1 2 3 4
0 0 1 2 3
0 0 0 1 2
0 0 0 0 1
0 0 0 0 0
Где я ошибаюсь? И есть ли более лаконичный метод (стримы)? upd. ошибка была в счетчике int count = i + 1 ; for (int j = 0; j < arr[i].length; j++) как это можно написать через стримы?И можно ли эту задачу сделать через List?
Ответы (3 шт):
Замечания и пожелания
- не объявляйте методы (
Matrix2) начинающиеся с большой буквы - это противоречит принятым в Java конвенциям - не пытайтесь запихнуть в один метод всю реализацию. Если Ваш метод берет на себя ответственность за ряд несвязанных между собой действий - разделите его на несколько. В частности у Вас в одном методе происходит и генерация матрицы, и ее вывод. А если кто-то захочет просто сгенерировать матрицу и передать ее куда-то? Зачем лишний выхлоп в поток вывода и лишние действия?
- называйте методы в соответствии с тем, что они делают. Желательно чтобы там присутствовал глагол( сгенерировать матрицу, распечатать матрицу и т.д.)
- старайтесь называть переменные в соответствии с их назначением. В будущем это сильно облегчит Вашу жизнь.
- если Вы производите какие-то хитрые манипцляции с данными, то можно их разделить на этапы, результат каждого из которых положив в переменную с "говорящим именем". Позже такой код будет гораздо легче читать и понимать, что у Вас там происходит. Для опытных разработчиков незазорно даже просто переложить что-то из переменной, название которой ничего не говорит в данном контексте в другую переменную, которая будет отражать её смысл и назначение при этом не производя какие-либо дополнительные манипуляций(
int salary = i;). Все за это только скажут спасибо.
Ваши ошибки
Вы начинаете вложенный цикл сразу со смещением
for (int j = i + 1; j < arr[i].length; j++) {
// ...
}
Соответственно, когда вы пытаетесь что-то положить в соответствующие ячейки
arr[i][j] = count++;
вы просто пропускаете все ячейки, которые выходят за рамки данного смещения.
т.е. при i = 2 вложенный цикл будет работать в интервале от 3 до 5 и пропускаете первые три ячейки, что мы и видем в вашем выводе:
0 0 0 1 2
Пояснение по моему решению
- мы проходим два вложенных цикла от 0 до указанной длины массива, не вкладывая в параметры цикла условия смещения - они тут лишние
- начало отчета внутри строки у нас равно i + 1
- смещение внутри строки у нас равно индексу вложенного цикла
- значение ячейки у нас равно
- сумме начала отчета и смещения
(если они меньше длины строки) - сумме начала отчета и смещения с вычетом длины строки
(если больше)
- сумме начала отчета и смещения
Решение
public class Matrix {
public static int[][] generateMatrix(int lenght) {
int[][] result = new int[lenght][lenght];
for (int i = 0; i < lenght; i++) {
for (int j = 0; j < lenght; j++) {
int start = i + 1;
int offset = j;
int value = start + offset;
result[i][j] = (value <= lenght) ? value : value - lenght;
}
}
return result;
}
public static void printMatrix(int[][] matrix) {
for (int[] row: matrix) {
System.out.println("\n");
for (int cellValue: row) {
System.out.print("\t" + cellValue);
}
}
}
public static void main(String[] args) {
int[][] matrix = generateMatrix(6);
printMatrix(matrix);
}
}
output:
1 2 3 4 5 6
2 3 4 5 6 1
3 4 5 6 1 2
4 5 6 1 2 3
5 6 1 2 3 4
6 1 2 3 4 5
И есть ли более лаконичный метод (стримы)?
Такой метод можно написать, даже с выводом матрицы при помощи Stream::peek с учётом реализации из прошлого ответа:
public static int[][] createFillAndPrintSquareArray(int n) {
return IntStream.range(0, n) // поток индексов строк от 0 до n
.mapToObj(i -> IntStream.range(0, n) // поток индексов колонок
.map(j -> 1 + (i + j) % n) // значение по индексу [i][j]
.peek(System.out::print) // вывод значения
.peek(j -> System.out.print('\t')) // вывод табуляции
.toArray() // массив-строка int[]
) // Stream<int[]> // поток массивов-строк
.peek(arr -> System.out.println()) // вывод новой строки
.toArray(int[][]::new); // получить двумерный массив
}
Однако, применение Stream::peek рекомендуется в основном для отладки, и к тому же не следует перегружать методы лишней функциональностью, как уже написал Михаил Ребров, поэтому более лаконичная реализация будет выглядеть так:
public static int[][] createAndFillSquareArray(int n) {
return IntStream.range(0, n) // поток индексов строк от 0 до n
.mapToObj(i -> IntStream.range(0, n) // поток индексов колонок
.map(j -> 1 + (i + j) % n) // значение по индексу [i][j]
.toArray() // массив-строка int[]
) // Stream<int[]> // поток массивов-строк
.toArray(int[][]::new); // получить двумерный массив
}
Я бы немного переписал, чтобы не было путанины с индексами и нулями.
public class MatrixVer2 {
public static int[][] Matrix2(int lenght) {
int[][] arr = new int[lenght][lenght];
int count = 1; // выносим вашу переменную вне циклов
int temp = 1; // создаем вспомогательную переменную
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
arr[i][j] = count++;
count = (count > arr.length) ? 1 : count;
}
count = ++b;
}
return arr;
}