1. 소개
먼저 질문부터 시작해 봅시다: 왜 반복문을 아예 다른 반복문 안에 넣어야 할까요? 종종 우리의 데이터나 과제가 단순히 일렬로만 조직되어 있지 않고, 예를 들어 표, 그리드, 혹은 다차원 구조로 구성되어 있기 때문입니다. 예를 들어 화면에 구구단을 출력하거나, 2차원 배열을 순회하거나, 모든 원소 쌍의 교차를 계산하고 싶다면 하나의 반복문만으로는 분명 부족합니다 — 반복문 안에 반복문이 필요합니다.
프로그래밍에서 중첩 반복문은 알람 두 개와 같습니다. 바깥 알람이 울리기 시작하면, 그 안에서 또 다른 알람이 시작되어 바깥 알람이 활성 상태인 동안 매번 울립니다. 즉, 하나의 “외부” 반복이 진행되는 동안 내부 반복은 자신의 전 범위를 모두 순회하고(그리고 외부의 각 반복마다 이를 계속 반복합니다).
좋은 예는 시와 분입니다. 시는 0부터 23까지의 외부 반복이고, 분은 0부터 59까지의 내부 반복입니다. 외부 반복이 한 번 바뀔 때마다 내부 반복은 자신의 모든 값을 빠르게 한 바퀴 돕니다.
2. 중첩 반복문의 문법
Java에서 중첩 반복문의 문법은 일반 반복문과 다르지 않습니다 — 단지 하나의 반복문을 다른 반복문의 본문 내부에 작성하면 됩니다. for와 while 예제를 살펴봅시다:
// 바깥 for 반복문
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++) // 안쪽 for 반복문
{
System.out.print(i + "," + j + " ");
}
System.out.println(); // 안쪽 반복문 뒤에 줄바꿈
}
여기서 외부 반복문은 변수 i를 제어합니다(0부터 2까지). 내부 반복문은 변수 j를 제어합니다(0부터 3까지). 각 i 값에 대해 내부 반복문은 j가 == 0에서 == 3까지 완전히 순회합니다. 이 코드를 실행하면 보기 좋은 좌표 표가 출력됩니다:
0,0 0,1 0,2 0,3
1,0 1,1 1,2 1,3
2,0 2,1 2,2 2,3
while을 사용한 동일한 예:
int i = 0;
while (i < 3)
{
int j = 0;
while (j < 4)
{
System.out.print(i + "," + j + " ");
j++;
}
System.out.println();
i++;
}
주의: 외부 반복문의 각 회차마다 내부 반복문의 변수(j)를 다시 초기화해야 합니다. 그렇지 않으면 단 한 줄만 보이게 됩니다!
3. 중첩 반복문의 예시
예시 1: 체스판 출력(8x8)
첫 번째 과제로, 흑백 칸으로 이뤄진 고전적인 체스판을 화면에 출력해 봅시다(‘#’ — 검은 칸, ‘.’ — 흰 칸이라고 하자). 중첩 for로 구현:
for (int row = 0; row < 8; row++)
{
for (int col = 0; col < 8; col++)
{
// 행 인덱스와 열 인덱스의 합이 짝수면 흰 칸, 아니면 검은 칸
if ((row + col) % 2 == 0)
System.out.print("_");
else
System.out.print("#");
}
System.out.println(); // 각 행이 끝날 때 줄바꿈
}
결과:
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
중요 포인트: 중첩 덕분에 각 행(row)마다 모든 열(col)을 완전히 순회합니다. 중첩이 없다면 보드 구조를 얻지 못하고 한 줄이나 한 열만 얻게 됩니다.
예시 2: 구구단
중첩 반복문으로 자주 등장하는 고전 예시입니다! 1–9 구구단을 출력해 봅시다:
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
System.out.print(i * j + "\t");
}
System.out.println();
}
포매팅인 i * j + "\t" 는 표가 가지런해 보이도록 탭 간격을 추가합니다.
결과:
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
...
9 18 27 36 45 54 63 72 81
4. 중첩 반복문과 그 제어 — 세부 사항
중첩 반복문에서 break와 continue의 영향
여기서 많은 초보자가 실수합니다! 내부 반복문에서 break나 continue를 사용하면, 그것들은 그 내부 반복문에만 영향을 줍니다. 외부 반복문은 아무 일 없었던 듯 계속 진행됩니다.
예: 내부 반복문에서만 조기 종료
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
if (j == 3)
break; // 내부 반복문에서만 빠져나갑니다!
System.out.print(i + "," + j + " ");
}
System.out.println();
}
결과:
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
만약 두 개의 중첩 반복문에서 한꺼번에 빠져나와야 한다면(예를 들어, 표에서 첫 일치 항목을 찾자마자 즉시 종료해야 할 때), 보통 플래그를 사용하거나 특수한 기법을 씁니다(모든 것이 함수 내부에서 일어난다면 return 등).
5. 중첩 반복문의 시각화
가끔은 중첩 반복문의 실행 순서를 “눈으로” 그리기 어렵습니다. 다음 순서도를 살펴봅시다:
표로 보면 — i가 1부터 3까지, j가 1부터 4까지일 때 총 몇 번의 반복이 일어날까요?
| i | j(각 i에 대해 순회되는 값) | 내부 반복문 반복 횟수 |
|---|---|---|
| 1 | 1, 2, 3, 4 | 4 |
| 2 | 1, 2, 3, 4 | 4 |
| 3 | 1, 2, 3, 4 | 4 |
| 합계: 3 × 4 = 12 |
6. 중첩 반복문 사용 시의 실수와 함정
흔한 실수는 내부 반복문의 변수를 잘못 초기화하는 것입니다. 예를 들어 그것을 외부 반복문 바깥에서 선언해 놓고 각 단계마다 초기화하지 않는 경우입니다. 그 결과 내부 반복문이 아예 실행되지 않거나 잘못 실행될 수 있습니다.
int j = 0;
for (int i = 0; i < 3; i++)
{
while (j < 4) // 이런! 첫 번째 반복 이후에는 j가 이미 4일 수 있습니다.
{
System.out.print(i + "," + j + " ");
j++;
}
System.out.println();
}
여기서는 반복이 단 한 번만 수행됩니다. 내부 반복문의 변수는 외부 반복문 안에서 다시 초기화해야 함을 잊지 마세요!
또한, 실수로 동일한 변수 이름을 가진 두 개의 중첩 반복문을 작성하면(for (int i = 0; ...) { for (int i = 0; ...) { ... } }), 컴파일러가 오류를 보고합니다: 해당 변수는 이미 정의되어 있습니다.
GO TO FULL VERSION