[아이티센 부트캠프] (다차원 배열)

이언덕·2026년 3월 21일

아이티센 부트캠프

목록 보기
8/115
post-thumbnail

[다차원 배열]

1. 다차원 배열이란

1-1. 1차원 배열과 무엇이 다른가

지금까지 본 일반 배열은 값을 한 줄로 저장하는 1차원 배열이다.
예를 들어 점수 5개를 저장하면 int[] score = new int[5];처럼 한 줄로 담는다.

그런데 데이터가 한 줄이 아니라 여러 줄로 구성되면 1차원 배열 하나로는 다루기 불편하다.
이럴 때 사용하는 것이 다차원 배열이다.

이번 범위에서 핵심은 2차원 배열이다.
2차원 배열은 쉽게 말해 배열 안에 또 배열이 들어 있는 구조다.

즉,

  • 1차원 배열은 값을 한 줄로 저장하고
  • 2차원 배열은 값을 여러 줄로 저장한다

고 이해하면 된다.



1-2. 행과 열로 이해하기

2차원 배열은 처음에는 표처럼 이해하면 가장 쉽다.
그래서 보통 행과 열로 나눠서 본다.

int[][] score = new int[4][3];

이 코드는 4행 3열 크기의 2차원 배열을 만든다는 뜻이다.
즉,

  • 4행 → 줄이 4개
  • 3열 → 각 줄마다 칸이 3개
    라고 보면 된다.






처음에는 표처럼 생각해도 괜찮다.

다만 자바의 2차원 배열은 실제로는 진짜 표 하나가 아니라 배열의 배열이라는 점을 뒤에서 다시 보게 된다.



1-3. 인덱스가 두 개인 이유

1차원 배열은 한 줄이기 때문에 인덱스가 하나면 된다.

nums[0]
nums[1]
nums[2]

하지만 2차원 배열은 몇 번째 줄인지, 그 줄의 몇 번째 칸인지를 함께 알아야 한다.
그래서 인덱스를 두 개 쓴다.

score[행][열]

예를 들어

score[1][2]

는 두 번째 행, 세 번째 열의 값이다.

여기서 많이 헷갈리는 부분이 있다.
배열 인덱스는 0부터 시작하므로 score[2][3]은 2행 3열이 아니라 세 번째 행, 네 번째 열이다.

즉,

  • score[0][0] → 1행 1열
  • score[1][2] → 2행 3열
  • score[2][3] → 3행 4열

이 그림은 score[행][열] 구조를 처음 이해할 때 가장 도움이 된다.




2. 2차원 배열의 선언, 생성, 초기화

2-1. 선언 방법 3가지

2차원 배열 선언은 아래 세 가지 방식이 모두 가능하다.

int[][] score;
int score[][];
int[] score[];

모양만 조금 다를 뿐 의미는 모두 같다.
전부 int형 2차원 배열을 선언한 것이다.

보통은 아래처럼 쓰는 경우가 가장 많다.

int[][] score;

이 방식이 2차원 배열이라는 점이 가장 눈에 잘 들어오기 때문이다.



2-2. 5행 3열 배열 만들기

2차원 배열을 실제로 만들 때는 new를 사용한다.

int[][] srcoe           // 2차원 배열 선언
score = new int[5][3];  // 2차원 배열 생성

or

int[][] score = new int[5][3]; // 2차월 배열 선언 + 생성

이 코드는 5행 3열 배열을 만든다.
즉, int값을 저장할 수 있는 칸이 15개 생긴다.

이때 배열을 만들기만 해도 각 칸이 비어 있는 것은 아니다.
자바는 배열을 생성하면 각 요소를 기본값으로 자동 초기화한다.

int의 기본값은 0이므로 아직 값을 따로 넣지 않았다면 전부 0이 들어 있다.



2-3. 선언과 생성을 한 뒤, 값을 나눠서 대입하는 방법

2차원 배열은 먼저 배열만 만든 뒤, 나중에 각 칸에 값을 따로 넣을 수도 있다.

int[][] score = new int[2][3];

score[0][0] = 100;
score[0][1] = 90;
score[0][2] = 80;

score[1][0] = 70;
score[1][1] = 60;
score[1][2] = 50;

이 방식은 배열의 구조를 먼저 만들고,
그다음 행 인덱스와 열 인덱스를 이용해 원하는 위치에 값을 넣는 방법이다.

즉,

  • score[0][0] = 100; → 첫 번째 행, 첫 번째 열에 100 저장
  • score[0][1] = 90; → 첫 번째 행, 두 번째 열에 90 저장
  • score[1][2] = 50; → 두 번째 행, 세 번째 열에 50 저장

이런 식으로 한 칸씩 직접 대입할 수 있다.

이 방식의 장점은 어느 위치에 어떤 값이 들어가는지 눈으로 확인하기 쉽다는 점이다.
반대로 칸 수가 많아지면 직접 하나씩 넣기 번거롭다는 점도 있다.

예제: TwoArrayTest1

int [][] score = new int [5][3]; // 5행의 3열 크기의 다차원배열 생성
System.out.println(score);
System.out.println(score[0]);    // 첫번째 행의 값 출력
System.out.println(score[0][0]); // 첫번째 행의 첫번째 열 출력
System.out.println();

for (int i = 0; i < score.length; i++) {
    for (int j = 0; j < score[i].length; j++) {
        System.out.print(score[i][j]);
    }
    System.out.println();
}

// 출력결과
// [[I@...
// [I@...
// 0
//
// 000
// 000
// 000
// 000
// 000

이 코드는 배열을 생성했을 때 바로 무엇이 보이는지 보여준다.

score를 출력하면 전체 2차원 배열의 참조 정보가 나온다.
score[0]을 출력하면 첫 번째 행 배열의 참조 정보가 나온다.
반면 score[0][0]은 실제 값 하나를 꺼낸 것이므로 0이 나온다.

그리고 아래 이중 for문을 보면 5행이기 때문에 줄은 5줄 나오고, 각 줄은 3열이기 때문에 0이 3개씩 출력된다.
값을 넣지 않았는데도 0이 나오는 이유는 int의 기본값이 0이기 때문이다.



2-3. 선언, 생성, 초기화를 한 번에 하는 방법

2차원 배열도 선언과 동시에 값을 넣을 수 있다.

int[][] score = {
    {100, 100, 100},
    {20, 30, 20}
};

or

int[][] score new int[][] = {
    {100, 100, 100},
    {20, 30, 20}
};

여기서 중괄호가 한 번 더 들어가는 이유는 각 행이 각각 하나의 배열이기 때문이다.

즉,

  • 바깥 중괄호 → 전체 2차원 배열
  • 안쪽 중괄호 → 각 행의 1차원 배열

이다.

초보자는 여기서 안쪽 중괄호가 왜 필요한지 헷갈릴 수 있는데,
2차원 배열은 결국 행 하나하나가 각각 배열이라고 생각하면 이해하기 쉽다.

예제: TwoArrayTest3

int[][] twoA = {
    {1,2,3},
    {4,5,6},
    {7,8,9},
    {0,0,0}
};

System.out.println(twoA);      // 행 배열들의 참조값
System.out.println(twoA[0]);   // 첫 번째 행 배열의 참조값
System.out.println(twoA[0][0]);// 1행 1열의 값

for(int row = 0; row < twoA.length; row++) {
    for(int col = 0; col < twoA[row].length; col++) {
        System.out.print(twoA[row][col] + "\t");
    }
    System.out.println();
}

// 출력결과
// [[I@...
// [I@...
// 1
// 1  2  3
// 4  5  6
// 7  8  9
// 0  0  0

이 코드는 선언과 동시에 값을 넣는 가장 기본적인 형태를 보여준다.
그리고 이중 for문으로 전체 데이터를 표처럼 출력한다.

여기서도 중요한 것은 바깥 반복이 행, 안쪽 반복이 열이라는 점이다.




3. 2차원 배열의 구조와 접근

3-1. score, score[0], score[0][0]의 차이

2차원 배열을 배울 때 가장 먼저 정확히 구분해야 하는 것이 이 세 가지다.

score
score[0]
score[0][0]

셋은 완전히 다르다.

  • score → 전체 2차원 배열
  • score[0] → 첫 번째 행 배열
  • score[0][0] → 첫 번째 행의 첫 번째 값

즉, score[0]은 값 하나가 아니라 한 줄 전체다.

그래서 System.out.println(score[0]);처럼 출력하면 값이 쭉 나오는 것이 아니라, 첫 번째 행 배열의 참조 정보가 나온다.
실제 값 하나를 꺼내려면 score[0][0]처럼 두 번 들어가야 한다.

또 score[2]의 크기는 얼마인지 생각해보면,
new int[5][3]으로 만들었으므로 세 번째 행의 길이는 3이다.

즉, score[2].length는 3이다.



3-2. score.length와 score[i].length의 의미

2차원 배열에서는 length도 구분해서 봐야 한다.

score.length

는 바깥 배열의 길이이므로 행의 개수다.


그리고

score[i].length

는 i번째 행 배열의 길이이므로 열의 개수다.

예제: TwoArrayTest2

int[] a4[] = new int[5][12]; // 5행 12열 생성

System.out.println(a4);
System.out.println(a4[0]);
System.out.println(a4[1]);
System.out.println(a4[2]);
System.out.println(a4[3]);
System.out.println(a4[4]);
System.out.println();

System.out.println(a4.length);
System.out.println(a4[0].length);
System.out.println(a4[1].length);
System.out.println(a4[2].length);
System.out.println(a4[3].length);
System.out.println(a4[4].length);

// 출력결과
// [[I@...
// [I@...
// [I@...
// [I@...
// [I@...
// [I@...
//
// 5
// 12
// 12
// 12
// 12
// 12

이 코드에서는

  • a4.length가 5
  • 각 행의 length가 12

로 나온다.

즉,

  • a4.length → 행이 5개
  • a4[0].length → 첫 번째 행의 열이 12개

라는 뜻이다.

초보자가 자주 헷갈리는 부분은 score.length를 열 개수라고 생각하는 것이다.
하지만 2차원 배열에서는 score.length가 행 개수라는 점을 먼저 잡아야 한다.



3-3. 표처럼 보이지만 실제로는 배열의 배열이다

겉으로 보면 2차원 배열은 네모난 표처럼 보인다.
그래서 처음에는 엑셀 표처럼 이해하기 쉽다.

하지만 자바에서 2차원 배열은 실제로 배열 안에 또 배열이 들어 있는 구조다.
즉, 여러 개의 1차원 배열을 다시 하나의 배열이 묶고 있는 형태다.


그래서

  • score는 각 행 배열들을 가리키고
  • score[0], score[1] 같은 값은 각각 한 행 배열을 가리키며
  • 그 안의 score[0][0], score[0][1] 같은 것이 실제 값이다

이렇게 이해해야 한다.

이런 구조 때문에 자바의 2차원 배열은 모든 행의 길이가 같을 필요가 없고, 그래서 가변 배열도 만들 수 있다.

이 그림은 바깥 배열이 각 행 배열의 참조를 들고 있다는 점을 보여줘서, 2차원 배열을 단순한 표가 아니라 배열의 배열로 이해하는 데 잘 맞는다.




4. 2차원 배열과 반복문

4-1. 왜 이중 for문을 써야 하는가

2차원 배열은 값이 여러 줄로 들어 있으므로 한 번의 반복문만으로는 전체를 다 돌 수 없다.
그래서 보통 이중 for문을 사용한다.

기본 형태는 아래와 같다.

for (int row = 0; row < score.length; row++) {
    for (int col = 0; col < score[row].length; col++) {
        System.out.print(score[row][col] + " ");
    }
    System.out.println();  // 여기에 println을 두게되면 표 형태로 가독성 좋게 결과값이 나오게 된다
}

이 구조는 꼭 익혀두는 것이 좋다.

  • 바깥 반복 → 행
  • 안쪽 반복 → 열

을 담당한다.



4-2. 바깥 반복과 안쪽 반복의 역할

바깥 for문은 몇 번째 행인지를 바꿔가며 반복한다.
안쪽 for문은 현재 행 안에서 몇 번째 열인지를 바꿔가며 반복한다.

즉, 흐름은 이렇게 된다.
1. 첫 번째 행 선택
2. 그 행의 열들을 처음부터 끝까지 출력
3. 줄바꿈
4. 다음 행 선택
5. 다시 그 행의 열들을 출력

이 순서 때문에 2차원 배열은 행 단위로 내려가면서 값이 출력된다.



4-3. 전체 데이터를 출력하는 방법

전체 데이터를 출력하려면 모든 행과 모든 열을 다 돌아야 한다.
그래서 안쪽 반복문 조건도 반드시 현재 행의 길이를 기준으로 써야 한다.

for (int row = 0; row < score.length; row++) {
    for (int col = 0; col < score[row].length; col++) {
        System.out.print(score[row][col] + " ");
    }
}

여기서 안쪽 반복 조건을 score.length로 쓰면 안 된다.
안쪽 반복은 열을 도는 것이므로 score[row].length를 써야 한다.

이 부분을 잘못 쓰면 열 개수만큼 정확히 반복하지 못한다.
특히 나중에 나오는 가변 배열에서는 이 차이가 더 중요해진다.



4-4. 특정 행과 특정 열만 다루는 방법

2차원 배열에서는 전체를 다 도는 경우도 있지만, 특정 행이나 특정 열만 따로 처리할 때도 많다.

예를 들어 마지막 행만 보고 싶다면 행을 고정하고 열만 바꾸면 된다.

for (int col = 0; col < score[마지막행].length; col++) {
    System.out.print(score[마지막행][col] + " ");
}System.out.println();

반대로 두 번째 열만 보고 싶다면 열을 고정하고 행만 바꾸면 된다.

for (int row = 0; row < score.length; row++) {
    System.out.print(score[row][1] + " ");
}

여기서도 조심할 점이 있다.
두 번째 열이라고 해서 인덱스를 2로 쓰면 안 된다.
배열 인덱스는 0부터 시작하므로 두 번째 열은 1이다.

즉,

  • 행을 고정하면 한 줄만 볼 수 있고
  • 열을 고정하면 같은 위치의 칸들을 세로로 볼 수 있다

는 감각을 잡아두면 된다.




5. 가변 배열

5-1. 가변 배열이란

가변 배열은 행마다 열의 개수가 다른 2차원 배열이다.

보통 new int[5][3]처럼 만들면 모든 행의 길이가 같다.
즉, 항상 3열씩 있는 직사각형 구조다.

그런데 자바의 2차원 배열은 실제로 배열의 배열이기 때문에
각 행이 가지는 열의 개수를 다르게 줄 수도 있다.

예를 들면

  • 첫 번째 행은 3칸 => 열의 크기가 3개
  • 두 번째 행은 4칸 => 열의 크기가 4개
  • 세 번째 행은 2칸 => 열의 크기가 2개

처럼 만들 수 있다.

이런 구조를 가변 배열이라고 한다.





5-2. new int[5][]의 의미

아래 코드는 처음 보면 조금 어색할 수 있다.

int[][] score = new int[5][];

이 코드는 2차원 배열이 맞다.
다만 아직 열의 길이를 정하지 않은 상태다.

즉,

  • 바깥쪽 5행은 만들어졌고
  • 각 행 안의 열 배열은 아직 연결되지 않았다

고 이해하면 된다.

그래서 이 상태에서 바로 score[0][0]처럼 값을 쓰려고 하면 안 된다.
먼저 score[0], score[1] 같은 각 행에 실제 배열을 연결해야 한다.

초보자는 여기서 이거 1차원 배열 아닌가? 하고 헷갈릴 수 있는데,
이건 이미 2차원 배열이고 마지막 차수만 나중에 정하는 형태일 뿐이다.



5-3. 행마다 열의 길이를 다르게 주는 방법

new int[5][]로 만든 뒤에는 각 행마다 따로 배열을 연결할 수 있다.

int[][] score = new int[5][];
score[0] = new int[3];
score[1] = new int[4];
score[2] = new int[5];
score[3] = new int[6];
score[4] = new int[7];

이렇게 하면 각 행이 가지는 열의 개수가 서로 달라진다.

즉,

  • 첫 번째 행의 열 개수 3
  • 두 번째 행의 열 개수 4
  • 세 번째 행의 열 개수 5
  • 네 번째 행의 열 개수 6
  • 다섯 번째 행의 열 개수 7

처럼 서로 다른 구조를 만들 수 있다.

여기서 바뀌는 것은 행의 개수가 아니라 각 행 안에 들어 있는 열의 길이다.
행의 개수는 처음에 new int[5][]로 만들 때 이미 5로 정해져 있고, 바뀌지 않는다.

이게 가능한 이유도 자바의 2차원 배열이 진짜 표 하나가 아니라,
1차원 배열 여러 개를 묶는 방식으로 되어 있기 때문이다.



5-4. 가변 배열을 순회할 때 주의할 점

가변 배열은 행마다 길이가 다르기 때문에 반복문을 더 조심해서 써야 한다.

예를 들어 첫 번째 행이 10칸, 두 번째 행이 20칸, 세 번째 행이 50칸일 수 있다.
이럴 때 안쪽 반복문을 모두 같은 횟수로 돌리면 안 된다.

반드시 현재 행 기준으로 써야 한다.

for (int row = 0; row < emp.length; row++) {
    for (int col = 0; col < emp[row].length; col++) {
        System.out.print(emp[row][col] + " ");
    }
    System.out.println();
}

예제: TwoArrayTest5

int[][] emp = new int[5][];
emp[0] = new int[10];
emp[1] = new int[20];
emp[2] = new int[50];
emp[3] = new int[10];
emp[4] = new int[30];

for(int row = 0; row < emp.length; row++) {
    for(int col = 0; col < emp[row].length; col++) {
        System.out.print(emp[row][col] + " ");
    }
    System.out.println();
}

// 출력결과
// 첫 번째 행: 0이 10개 출력
// 두 번째 행: 0이 20개 출력
// 세 번째 행: 0이 50개 출력
// 네 번째 행: 0이 10개 출력
// 다섯 번째 행: 0이 30개 출력

이 예제에서 중요한 것은 값보다 행마다 반복 횟수가 달라진다는 점이다.

각 행마다 길이가 다르기 때문에

  • 첫 번째 줄은 10번 반복
  • 두 번째 줄은 20번 반복
  • 세 번째 줄은 50번 반복

된다.

또 아직 값을 따로 넣지 않았으므로 int의 기본값인 0이 출력된다.

여기서 emp[0] = new int[10];을 보고 값을 넣는 코드라고 착각하기 쉬운데,
이건 값 초기화가 아니라 첫 번째 행의 열 크기를 10으로 정한 것이다.




6. 응용 예제 1 - 2차원 배열의 전체 합, 행의 합, 열의 합 구하기

문제에서 무엇을 하려는지

이 예제는 2차원 배열 안에 들어 있는 값을 여러 방식으로 더해 보는 연습이다.
전체 데이터의 합을 구하고, 마지막 행의 합과 두 번째 열의 합까지 따로 구하면서 행과 열을 어떻게 나눠서 다루는지 복습하는 것이 핵심이다.

이 예제에서 사용된 개념

  • 2차원 배열
  • 행 인덱스, 열 인덱스
  • length
  • 이중 for문
  • 특정 행 고정
  • 특정 열 고정
  • 가변 배열



코드 흐름

먼저 2차원 배열 안에 여러 줄의 값을 저장한다.
이 배열은 행마다 길이가 서로 다르므로 가변 배열 형태다.

그다음 이중 for문을 사용해서 모든 행과 모든 열을 돌며 전체 합을 구한다.

이후에는 마지막 행을 하나 고정한 뒤, 그 행 안의 열만 반복해서 마지막 행의 합을 구한다.

마지막으로는 두 번째 열을 고정하고 각 행을 차례대로 돌면서 해당 열의 값만 더한다.

즉, 같은 2차원 배열이라도 전체를 돌 때, 행 하나만 볼 때, 열 하나만 볼 때 반복 방식이 달라진다는 점을 보여주는 예제다.

코드 예제

예제: TwoArrayTest4

int nums[][] = {
    {10, 20, 30},
    {11, 21, 31, 41},
    {12, 22}
};

int sum = 0;
for(int i = 0; i < nums.length; i++) {
    for(int j = 0; j < nums[i].length; j++) {
        sum += nums[i][j];
    }
}
System.out.println("전체 데이터의 합 : " + sum);

int sumRow = 0;
// 마지막 행의 데이터만 합산
for(int col = 0; col < nums[2].length; col++) {
    sumRow += nums[2][col];
}

int sumCol = 0;
// 두 번째 열의 데이터만 합산
for(int row = 0; row < nums.length; row++) {
    sumCol += nums[row][1];
}

System.out.println("마지막 행 데이터의 합 : " + sumRow);
System.out.println("두번째 열 데이터의 합 : " + sumCol);

// 출력결과
// 전체 데이터의 합 : 198
// 마지막 행 데이터의 합 : 34
// 두번째 열 데이터의 합 : 63

헷갈리기 쉬운 부분

전체 합을 구할 때 안쪽 반복문의 조건은 반드시 nums[i].length로 써야 한다.
안쪽 반복문은 현재 행의 열을 도는 것이므로, 현재 행 기준 길이를 써야 한다.

또 두번째 열이라고 해서 인덱스를 2로 쓰면 안 된다.
배열 인덱스는 0부터 시작하므로 두 번째 열은 1이다.

그리고 이 배열은 행마다 길이가 다르므로, 모든 행의 열 개수가 같다고 생각하면 안 된다.
이 점 때문에 가변 배열에서는 반복문의 조건을 더 정확하게 써야 한다.

0개의 댓글