다차원 배열과 행의 길이
Java의 다차원 배열은 하나의 직사각형 메모리 블록이 아니라 배열을 원소로 갖는 배열입니다.
따라서 각 행의 길이가 서로 다른 톱니 모양 구조를 만들 수 있습니다.
바깥 배열과 안쪽 배열은 각각 별도의 객체이며 생성 시점도 나눌 수 있습니다.
행마다 길이를 확인하지 않고 첫 행의 길이를 모든 행에 적용하면 범위 오류가 납니다.
이 절에서는 좌표처럼 보이는 문법 뒤의 참조 구조를 살펴봅니다.
2차원 배열은 배열의 배열이다
int[][] grid의 각 원소는 int[] 참조입니다.
바깥 길이는 행 수이고 grid[row].length는 해당 행의 열 수입니다.
직사각형과 톱니 배열을 구분한다
new int[3][4]는 같은 길이의 행을 만들지만 바깥 배열만 만든 뒤 행마다 다른 길이를 대입할 수도 있습니다.
알고리즘이 어느 형태를 전제하는지 명시합니다.
JaggedArrays.java는 길이가 다른 행을 의도적으로 만들어 각 행의 독립성을 드러냅니다. 좌표 표기만 보고 직사각형이라고 가정하지 말고 바깥 배열에서 현재 행을 먼저 꺼내 읽습니다.
import java.util.Arrays;
public class JaggedArrays {
public static void main(String[] args) {
int[][] rows = {{1, 2}, {3, 4, 5}, {6}};
int total = 0;
for (int row = 0; row < rows.length; row++) {
for (int column = 0; column < rows[row].length; column++) {
total += rows[row][column];
}
System.out.println(Arrays.toString(rows[row]));
}
System.out.println("total=" + total);
}
}각 행의 길이가 다른 출력은 2차원 배열이 하나의 직사각형 메모리 블록이 아니라 int[] 참조를 담은 배열이라는 사실을 보여 줍니다. 따라서 행마다 열의 유효 범위가 따로 있습니다.
[1, 2]
[3, 4, 5]
[6]
total=21각 행의 length를 사용하므로 길이가 서로 다른 배열도 안전하게 순회합니다.
int[] currentRow = grid[row]처럼 바깥 배열에서 행 참조를 꺼냅니다. null 행을 허용하지 않는 계약이라면 이 경계에서 검증합니다.
안쪽 반복의 상한은 첫 행이 아니라 currentRow.length입니다. 톱니 배열에서는 이 값이 행마다 달라질 수 있습니다.
0부터 현재 행 길이 직전까지 순회합니다. 행과 열 좌표가 필요 없으면 for (int value : currentRow)로 경계를 반복문에 맡길 수 있습니다.
중첩 반복은 현재 행의 길이를 사용한다
안쪽 조건에 grid[row].length를 사용하면 톱니 배열도 안전합니다.
grid[0].length를 고정해서 쓰면 짧은 행에서 실패합니다.
안쪽 반복 조건을 rows[0].length로 고정하면 더 짧은 행에서 범위를 벗어나고 더 긴 행의 뒤쪽은 놓칩니다. 반드시 현재 행인 rows[row].length를 사용하거나 향상된 for로 행과 원소를 직접 순회합니다.
int[][] original = {{1, 2}, {3}};
int[][] shallow = original.clone();
shallow[0][0] = 99; // 안쪽 행은 공유됨int[][] deep = new int[original.length][];
for (int row = 0; row < original.length; row++) {
deep[row] = original[row].clone();
}
// System.out.println(original[1][1]); // 실행 시 두 번째 행 길이는 1얕은 복사와 깊은 복사를 나눈다
바깥 배열만 복사하면 안쪽 행 참조는 여전히 공유됩니다.
모든 행을 독립적으로 만들려면 행마다 복사해야 합니다.
행 자체가 null일 가능성을 정한다
바깥 배열의 원소는 참조이므로 특정 행이 생성되지 않으면 null일 수 있습니다.
모든 행 존재를 생성 시 보장하거나 순회 전에 검사합니다.
톱니 배열은 행 참조 자체가 null일 수도 있습니다. 데이터 계약에서 null 행을 금지해 생성 시 검사할지, 순회 때 빈 행처럼 건너뛸지 먼저 정해야 곳곳에 임의의 null 처리가 퍼지지 않습니다.
다차원 배열을 좌표 표가 아니라 참조가 한 단계 더 있는 구조로 보면 행 길이와 복사 문제를 쉽게 설명할 수 있습니다.