본문으로 건너뛰기

5.3 다차원 배열 (Multi-dimensional Arrays)

지금까지 배운 1차원 배열은 요소들이 일직선으로 연속 배치된 형태입니다. 자바에서는 [][] 등 대괄호를 추가하여 2차원, 3차원 이상의 복잡한 데이터 구조를 만들 수 있습니다. 표, 행렬, 이미지 데이터 등을 표현할 때 활용합니다.

1. 2차원 배열의 선언과 생성

2차원 배열은 행(Row)과 열(Column) 로 구성된 표 형태의 데이터를 저장할 때 주로 사용합니다.

선언 방법

// 타입[][] 변수명 (권장)
int[][] score;
String[][] table;

// 생성 (4행 3열)
int[][] score = new int[4][3]; // 총 12개 공간

초기화와 인덱스 구조

// 직접 값으로 초기화
int[][] score = {
{ 100, 90, 80 }, // 0행
{ 70, 85, 95 }, // 1행
{ 60, 75, 88 }, // 2행
{ 55, 65, 70 } // 3행
};

// 인덱스: score[행][열]
System.out.println(score[0][0]); // 100 (0행 0열)
System.out.println(score[1][2]); // 95 (1행 2열)
System.out.println(score[3][1]); // 65 (3행 1열)

2. 2차원 배열의 메모리 구조

자바에서 2차원 배열은 내부적으로 "배열의 배열" 구조입니다.

[스택]         [힙]
score ────→ [주소0][주소1][주소2][주소3] ← 행 참조 배열
| | | |
↓ ↓ ↓ ↓
[100,90,80] [70,85,95] [60,75,88] [55,65,70]

이 구조 덕분에:

  • score.length: 행의 개수 (4)
  • score[0].length: 0번째 행의 열 개수 (3)
int[][] score = new int[4][3];

System.out.println("행의 수: " + score.length); // 4
System.out.println("0행의 열 수: " + score[0].length); // 3

3. 2차원 배열 순회

이중 for 문을 사용합니다.

int[][] score = {
{ 100, 90, 80 },
{ 70, 85, 95 },
{ 60, 75, 88 },
{ 55, 65, 70 }
};

// 일반 for 문
for (int i = 0; i < score.length; i++) { // 행 반복
for (int j = 0; j < score[i].length; j++) { // 열 반복
System.out.printf("score[%d][%d]=%3d ", i, j, score[i][j]);
}
System.out.println();
}

System.out.println();

// 향상된 for 문
for (int[] row : score) { // 각 행(1차원 배열)을 순회
for (int val : row) { // 각 행의 요소를 순회
System.out.printf("%4d", val);
}
System.out.println();
}

출력:

score[0][0]=100  score[0][1]= 90  score[0][2]= 80
score[1][0]= 70 score[1][1]= 85 score[1][2]= 95
...

4. 가변 배열 (Jagged Array)

자바의 2차원 배열은 "배열의 배열" 구조이므로 각 행의 길이가 서로 달라도 됩니다. 이런 배열을 가변 배열(Jagged Array)이라고 합니다.

// 각 행의 크기를 다르게 지정
int[][] jagged = new int[4][]; // 행 수만 지정
jagged[0] = new int[1]; // 0행: 1개
jagged[1] = new int[2]; // 1행: 2개
jagged[2] = new int[3]; // 2행: 3개
jagged[3] = new int[4]; // 3행: 4개

// 값 채우기
int value = 1;
for (int i = 0; i < jagged.length; i++) {
for (int j = 0; j < jagged[i].length; j++) {
jagged[i][j] = value++;
}
}

// 출력
for (int[] row : jagged) {
for (int val : row) {
System.out.printf("%3d", val);
}
System.out.println();
}

출력:

  1
2 3
4 5 6
7 8 9 10

가변 배열 활용 사례: 삼각형 행렬

// 파스칼의 삼각형 (5행)
int[][] pascal = new int[5][];
for (int i = 0; i < pascal.length; i++) {
pascal[i] = new int[i + 1];
pascal[i][0] = 1; // 각 행의 첫 번째
pascal[i][i] = 1; // 각 행의 마지막
for (int j = 1; j < i; j++) {
pascal[i][j] = pascal[i-1][j-1] + pascal[i-1][j];
}
}

for (int[] row : pascal) {
for (int val : row) {
System.out.printf("%4d", val);
}
System.out.println();
}

출력:

   1
1 1
1 2 1
1 3 3 1
1 4 6 4 1

5. 3차원 배열

3차원 배열은 [][][]를 사용합니다. 실용적인 사례로는 RGB 이미지 데이터나 3D 공간 데이터가 있습니다.

// 3차원 배열: [깊이][행][열]
int[][][] cube = new int[2][3][4]; // 2층, 3행, 4열

// 값 대입
for (int d = 0; d < cube.length; d++) {
for (int r = 0; r < cube[d].length; r++) {
for (int c = 0; c < cube[d][r].length; c++) {
cube[d][r][c] = d * 100 + r * 10 + c;
}
}
}

System.out.println(cube[0][1][2]); // 012 = 12
System.out.println(cube[1][2][3]); // 123

실용 예: RGB 이미지 픽셀 데이터

// 간단한 3x3 이미지의 RGB 값 저장
// pixels[행][열][채널] - 채널: 0=R, 1=G, 2=B
int[][][] pixels = new int[3][3][3];

// 빨간 점 (0, 0) 설정
pixels[0][0][0] = 255; // R
pixels[0][0][1] = 0; // G
pixels[0][0][2] = 0; // B

// 초록 점 (1, 1) 설정
pixels[1][1][0] = 0;
pixels[1][1][1] = 255;
pixels[1][1][2] = 0;

System.out.println("(0,0) RGB = (" +
pixels[0][0][0] + ", " + pixels[0][0][1] + ", " + pixels[0][0][2] + ")");
// 출력: (0,0) RGB = (255, 0, 0)

6. 2차원 배열 vs ArrayList<ArrayList<T>>

비교 항목int[][]ArrayList<ArrayList<Integer>>
크기 변경불가가능
메모리 효율높음낮음 (오버헤드)
접근 속도빠름상대적으로 느림
편의 메서드없음풍부 (add, remove 등)
기본형 사용가능 (int)불가 (Integer로 박싱)
// ArrayList<ArrayList<Integer>> 사용 예
import java.util.ArrayList;

ArrayList<ArrayList<Integer>> matrix = new ArrayList<>();
for (int i = 0; i < 3; i++) {
ArrayList<Integer> row = new ArrayList<>();
for (int j = 0; j < 3; j++) {
row.add(i * 3 + j);
}
matrix.add(row);
}

System.out.println(matrix.get(1).get(2)); // 5
선택 기준
  • 크기가 고정 이고 성능 이 중요하면: int[][]
  • 크기가 동적으로 변하거나 행마다 다를 수 있으면: ArrayList<ArrayList<Integer>>

7. 실전 예제 1: 3x3 행렬 덧셈과 곱셈

import java.util.Arrays;

public class MatrixOperations {
// 행렬 출력
static void printMatrix(int[][] m) {
for (int[] row : m) {
System.out.println(Arrays.toString(row));
}
}

// 행렬 덧셈
static int[][] add(int[][] a, int[][] b) {
int rows = a.length;
int cols = a[0].length;
int[][] result = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result[i][j] = a[i][j] + b[i][j];
}
}
return result;
}

// 행렬 곱셈 (n×m * m×k = n×k)
static int[][] multiply(int[][] a, int[][] b) {
int n = a.length;
int m = a[0].length; // = b.length
int k = b[0].length;
int[][] result = new int[n][k];
for (int i = 0; i < n; i++) {
for (int j = 0; j < k; j++) {
for (int p = 0; p < m; p++) {
result[i][j] += a[i][p] * b[p][j];
}
}
}
return result;
}

public static void main(String[] args) {
int[][] a = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int[][] b = {
{9, 8, 7},
{6, 5, 4},
{3, 2, 1}
};

System.out.println("행렬 A:");
printMatrix(a);

System.out.println("\n행렬 B:");
printMatrix(b);

System.out.println("\nA + B:");
printMatrix(add(a, b));

System.out.println("\nA * B:");
printMatrix(multiply(a, b));
}
}

출력:

행렬 A:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

행렬 B:
[9, 8, 7]
[6, 5, 4]
[3, 2, 1]

A + B:
[10, 10, 10]
[10, 10, 10]
[10, 10, 10]

A * B:
[30, 24, 18]
[84, 69, 54]
[138, 114, 90]

8. 실전 예제 2: 달력 출력 프로그램

2차원 배열로 7열(요일) 달력을 구성하는 예제입니다.

public class CalendarPrinter {
public static void main(String[] args) {
// 2025년 3월: 1일이 토요일(6), 31일까지
int startDay = 6; // 0=일, 1=월, ..., 6=토
int totalDays = 31;

// 6행 7열 달력 배열 (빈 칸은 0)
int[][] calendar = new int[6][7];
int day = 1;

for (int row = 0; row < 6; row++) {
for (int col = 0; col < 7; col++) {
if (row == 0 && col < startDay) {
calendar[row][col] = 0; // 빈 칸
} else if (day <= totalDays) {
calendar[row][col] = day++;
}
}
}

// 달력 출력
System.out.println(" 2025년 3월");
System.out.println("일 월 화 수 목 금 토");
System.out.println("---------------------------");

for (int[] week : calendar) {
for (int d : week) {
if (d == 0) {
System.out.print(" "); // 빈 칸
} else {
System.out.printf("%3d ", d);
}
}
// 마지막 날 이후 행은 출력 안 함
boolean hasValue = false;
for (int d : week) if (d > 0) hasValue = true;
if (hasValue) System.out.println();
}
}
}

출력:

    2025년 3월
일 월 화 수 목 금 토
---------------------------
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
고수 팁

다차원 배열의 Arrays.deepToString()으로 2차원 배열 내용을 간편하게 확인할 수 있습니다.

import java.util.Arrays;

int[][] matrix = {{1, 2}, {3, 4}, {5, 6}};

System.out.println(Arrays.toString(matrix)); // 주소값 출력 (잘못된 방법)
System.out.println(Arrays.deepToString(matrix)); // [[1, 2], [3, 4], [5, 6]]

2차원 배열 깊은 복사(deep copy)는 각 행을 개별적으로 복사해야 합니다.

int[][] original = {{1, 2, 3}, {4, 5, 6}};
int[][] deepCopy = new int[original.length][];
for (int i = 0; i < original.length; i++) {
deepCopy[i] = original[i].clone(); // 각 행을 개별 복사
}