선언 방식은 1차원 배열과 같으나, 2차원 배열은 배열의 배열이기 때문에 대괄호[]
가 하나 더 붙음.
2차원 배열에는 주로 테이블 형태의 데이터를 담게 됨.
타입[][] 변수명
예시)
int[][] exampleTable = new int[2][3]; // 2행 3열의 2차원 배열 생성
선언만 한 상태에서는 모든 원소가 int
원소의 기본값인 0
몇번째 행의 몇번째 열에 있는 원소인지 둘 다 표기해야 함
배열이름[행 인덱스][열 인덱스]
예시)
int exampleElement = exampleTable[1][2]; // exampleTable 배열의 1행 2열에 위치한 원소
1차원 배열과 마찬가지로 중괄호{}
를 써서 생성과 초기화를 동시에 할 수 있음
int[][] arr = new int[][]{{1, 2, 3}, {4, 5, 6}}; // 2행 3열의 2차원 배열 생성
// 또는
int[][] arr = {{1, 2, 3}, {4, 5, 6}}; // new int[][] 생략 가능
2차원 배열의 원소들을 훑을 때 2중 for
문을 돌리게 되는데,
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = 1; // 모든 원소의 값을 1로 초기화
}
}
이러한 형식은 향상된 for
문을 활용해 조금 더 간략하게 표현할 수 있음
for (int[] row : arr) {
for (int el : row) {
el = 1;
}
}
대신 이 방법은 각 원소의 인덱스에 접근할 수 없으므로 특정 원소의 값을 수정하는데에는 적절치 않음.
2차원 배열은 결국 배열의 배열이라는 점을 미루어보아, 다차원 배열을 좀 더 유동적이게 만들 수 있음.
다차원 배열의 맨 마지막 차수 길이를 비워놓으면 추가 배열을 생성할 때마다 다른 길이로 만들어 유동성을 줄 수 있는데 이를 가변 배열이라고 함.
예시) 3행짜리 2차원 가변 배열
int[][] flexibleArr = new int[3][]; // 두번째 차수의 길이는 일부러 비워둠
flexibleArr[0] = new int[3];
flexibleArr[1] = new int[1];
flexibleArr[2] = new int[2];
위 예시를 테이블로 나타내보면
[0][0] | [0][1] | [0][2] |
[1][0] | ||
[2][0] | [2][1] |
가변 배열도 중괄호{}
를 사용해 간편하게 초기화 할 수 있음
int[][] numbers = {
{1, 7, 4},
{5},
{2, 6}
};
교재에 제시된 예제로 실습
교재에 나온 배 찾기 게임을 변형한 보물찾기 게임. (gameBoard 상에서 0은 꽝, 1은 보물)
public class MultiArrayExample1 {
public static void main(String[] args) {
byte[][] gameBoard = {
{0, 1, 0, 0, 1},
{1, 0, 1, 1, 0},
{1, 1, 0, 1, 1},
{0, 1, 0, 1, 0},
{1, 0, 0, 0, 1},
}; // 보물과 꽝 위치가 표시된 정답지 2차원 배열
final int SIZE = gameBoard.length + 1;
char[][] guideBoard = new char[SIZE][SIZE]; // 유저의 입력을 받았을 때 O, X를 마킹해줄 2차원 배열
for (int i = 1; i < SIZE; i++) {
guideBoard[0][i] = guideBoard[i][0] = (char)(i + '0'); // guideBoard의 첫 행과 첫 열은 좌표를 알아보기 쉽게 행 넘버, 열 넘버 표시
}
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.printf("1 ~ %d 사이의 숫자로 이루어진 좌표를 입력하세요. (종료는 exit 입력): ", (SIZE - 1));
String userInput = scanner.nextLine();
int x = userInput.charAt(0) - '0'; // 문자를 숫자로 바꾸기 위해 '0'(ASCII 48)을 빼줌
int y = userInput.charAt(1) - '0';
boolean outOfRange = x <= 0 || x > SIZE || y <= 0 || y > SIZE; // 입력받을 x와 y 좌표의 범위 이탈 조건
if (userInput.equals("exit")) {
break;
}
if (!userInput.equals("exit") && (userInput.length() != 2 || outOfRange)) {
System.out.println("잘못된 입력입니다. 다시 입력해주세요!");
continue; // while문 맨 처음으로 이동
}
guideBoard[x][y] = gameBoard[x-1][y-1] == 1 ? 'O' : 'X'; // 입력한 자리에 보물이 있다면 O, 꽝이면 X 마킹
for (int i = 0; i < SIZE; i++) {
System.out.println(guideBoard[i]); // 마킹이 반영된 2차원 배열을 콘솔에 출력하여 표시
}
}
}
}
2차원 배열을 하나의 행렬이라고 생각하고 행렬의 곱셈처럼 곱 연산을 할 수 있음.
교재 풀이에서는 아래처럼 3중
for
문을 썼지만, 곱하기 전에 먼저 2번째 피연산 행렬을 뒤집어놓으면 2중for
문으로 해결 가능
public class MultiArrayExample2 {
public static void main(String[] args) {
int[][] m1 = {
{5, 1, 4},
{2, 6, 3}
}; // 3x2 행렬
int[][] m2 = {
{3, 6},
{2, 1},
{4, 5}
}; // 2x3 행렬
final int ROW = m1.length; // 행의 개수 (2)
final int COLUMN = m2[0].length; // 열의 개수 (2)
int[][] m3 = new int[ROW][COLUMN]; // 곱의 결과로 나온 배열 m3
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COLUMN; j++) {
for (int k = 0; k < m2.length; k++) {
m3[i][j] += m1[i][k] * m2[k][j]; // m3의 원소에 m1, m2 원소들의 곱이 더해지게 함
}
}
} // 이러한 3중첩 for문은 연산 효율이 기하급수적으로 떨어지므로 되도록 지양...
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COLUMN; j++) {
System.out.printf("%d ", m3[i][j]); // 한 행 안에서 모든 원소 출력
}
System.out.println(); // 한 행이 마무리되면 줄 바꿈
}
}
}
교재에는 없으나 알아두면 유용할 것 같아서 함.
public class IntChar {
public static void main(String[] args) {
int[][] m1 = {
{9, 2, 5, 10},
{4, 8, 12, 1},
{11, 7, 6, 3},
}; // 4x3 행렬
final int ROW = m1.length;
final int COLUMN = m1[0].length;
int[][] m2 = new int[COLUMN][ROW]; // 행과 열을 뒤집은 결과를 담을 배열
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COLUMN; j++) {
m2[j][i] = m1[i][j]; // m1과 m2는 같은 원소에 대해 행과 열의 좌표가 반대임
}
}
for (int i = 0; i < COLUMN; i++) {
for (int j = 0; j < ROW; j++) {
System.out.printf("%d ", m2[i][j]); // m2 각 행의 원소 출력
}
System.out.println(); // 한 행이 끝나면 줄바꿈
}
}
}