같은 종류의 값을 여러 개 다룰 때 변수를 하나씩 만들면 코드가 빠르게 길어집니다. 배열은 같은 타입의 값을 하나의 묶음으로 관리하는 자료구조입니다.

이번 글에서는 배열을 만드는 방법부터 인덱스, length, 반복문, 2차원 배열까지 차례대로 정리합니다. 반복문이 아직 익숙하지 않다면 자바 반복문 정리 - while부터 for까지를 먼저 확인하는 편이 좋습니다.


배열이 필요한 이유

일주일 동안 측정한 온도를 변수로 저장한다고 생각해보겠습니다.

int monday = 24;
int tuesday = 26;
int wednesday = 25;
int thursday = 27;
int friday = 23;

값이 다섯 개뿐이라면 작성할 수 있지만, 한 달이나 일 년의 기록을 다루면 변수도 계속 늘어납니다. 합계나 평균을 구할 때도 모든 변수 이름을 직접 사용해야 합니다.

배열을 사용하면 같은 성격의 값을 하나로 묶을 수 있습니다.

int[] temperatures = {24, 26, 25, 27, 23};

이제 각 값은 temperatures라는 배열 하나에 들어 있습니다. 반복문과 함께 사용하면 값의 개수가 늘어나도 같은 코드로 처리할 수 있습니다.


배열 선언과 생성

배열은 선언생성을 나누어 작성할 수 있습니다.

int[] temperatures;
temperatures = new int[5];

int[] temperatures는 정수 배열을 가리킬 변수를 선언합니다. new int[5]는 정수 다섯 개를 담을 배열을 새로 만듭니다.

두 줄을 한 줄로 합칠 수도 있습니다.

int[] temperatures = new int[5];

여기서 temperatures에 배열의 값 전체가 직접 들어가는 것은 아닙니다. 배열이 만들어진 위치를 가리키는 참조값이 저장됩니다.

기본값

new로 배열을 만들면 각 요소는 타입에 맞는 기본값으로 채워집니다.

배열 타입기본값
int[]0
double[]0.0
boolean[]false
String[]null
int[] numbers = new int[3];

System.out.println(numbers[0]); // 0
System.out.println(numbers[1]); // 0
System.out.println(numbers[2]); // 0

지역 변수는 값을 넣지 않으면 사용할 수 없지만, 배열의 요소는 생성할 때 기본값으로 초기화됩니다.


인덱스로 값에 접근하기

배열 안의 각 위치를 나타내는 번호를 인덱스라고 합니다. 자바 배열의 인덱스는 0부터 시작합니다.

int[] temperatures = new int[5];

temperatures[0] = 24;
temperatures[1] = 26;
temperatures[2] = 25;
temperatures[3] = 27;
temperatures[4] = 23;

배열의 첫 번째 값은 temperatures[0], 마지막 값은 temperatures[4]입니다. 배열 길이가 5라면 사용할 수 있는 인덱스는 0부터 4까지입니다.

값:       24  26  25  27  23
인덱스:    0   1   2   3   4

존재하지 않는 인덱스에 접근하면 실행 중 오류가 발생합니다.

System.out.println(temperatures[5]);
ArrayIndexOutOfBoundsException

길이가 n인 배열에서 사용할 수 있는 마지막 인덱스는 항상 n - 1입니다.


배열을 바로 초기화하기

배열을 만들면서 값을 함께 넣을 수 있습니다.

int[] temperatures = new int[]{24, 26, 25, 27, 23};

선언과 초기화를 같은 줄에서 한다면 new int[]를 생략할 수 있습니다.

int[] temperatures = {24, 26, 25, 27, 23};

값이 이미 정해져 있다면 이 형태가 가장 간결합니다. 배열 길이도 넣은 값의 개수에 맞춰 자동으로 결정됩니다.

다만 선언을 먼저 끝낸 뒤에는 축약형을 사용할 수 없습니다.

int[] temperatures;
temperatures = new int[]{24, 26, 25, 27, 23}; // 가능
int[] temperatures;
temperatures = {24, 26, 25, 27, 23}; // 컴파일 오류

length와 반복문 사용하기

배열의 길이는 length로 확인합니다.

int[] temperatures = {24, 26, 25, 27, 23};

System.out.println(temperatures.length); // 5

배열의 모든 값을 읽을 때는 for문과 length를 함께 사용하는 것이 안전합니다.

int[] temperatures = {24, 26, 25, 27, 23};

for (int i = 0; i < temperatures.length; i++) {
    System.out.println(i + "번째 인덱스: " + temperatures[i]);
}

반복 조건에 숫자 5를 직접 쓰지 않았습니다. 나중에 배열 길이가 달라져도 반복문을 수정할 필요가 없습니다.

합계와 평균 구하기

배열과 반복문을 이용해 주간 평균 온도를 구해보겠습니다.

int[] temperatures = {24, 26, 25, 27, 23};
int total = 0;

for (int i = 0; i < temperatures.length; i++) {
    total += temperatures[i];
}

double average = (double) total / temperatures.length;

System.out.println("합계: " + total);
System.out.println("평균: " + average);

실행 결과입니다.

합계: 125
평균: 25.0

평균을 소수로 계산하려면 합계를 double로 형변환해야 합니다. 정수끼리 나누면 소수점 아래 값이 사라지기 때문입니다.


향상된 for문

배열의 값을 처음부터 끝까지 읽기만 한다면 향상된 for을 사용할 수 있습니다. for-each문이라고도 부릅니다.

int[] temperatures = {24, 26, 25, 27, 23};

for (int temperature : temperatures) {
    System.out.println(temperature);
}

반복할 때마다 배열의 값 하나가 temperature에 들어갑니다. 인덱스 변수와 종료 조건을 직접 작성하지 않아 코드가 짧습니다.

하지만 현재 인덱스가 필요하다면 일반 for문이 더 적절합니다.

for (int i = 0; i < temperatures.length; i++) {
    System.out.println((i + 1) + "일차 온도: " + temperatures[i]);
}
상황적절한 반복문
값만 순서대로 읽기향상된 for
인덱스가 필요함일반 for
일부 위치의 값을 변경함일반 for
뒤에서부터 순회함일반 for

2차원 배열

1차원 배열이 값의 한 줄이라면, 2차원 배열은 행과 열로 이루어진 표에 가깝습니다. 극장 좌석의 예약 상태를 저장하는 예제를 보겠습니다.

boolean[][] seats = new boolean[3][4];

이 배열은 3개의 행과 4개의 열을 가집니다. 특정 좌석에 접근할 때는 배열[행][열] 형태를 사용합니다.

seats[0][1] = true;
seats[2][3] = true;

System.out.println(seats[0][1]); // true
System.out.println(seats[1][1]); // false

2차원 배열도 값을 넣으면서 초기화할 수 있습니다.

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

board.length는 행의 개수입니다. board[row].length는 현재 행에 들어 있는 열의 개수입니다.


중첩 반복문으로 2차원 배열 순회하기

2차원 배열 전체를 읽을 때는 반복문을 두 번 사용합니다. 바깥 반복문은 행을 이동하고, 안쪽 반복문은 현재 행의 열을 이동합니다.

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

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

실행 결과입니다.

1 2 3
4 5 6
7 8 9

열의 길이를 board[0].length로 고정하지 않고 board[row].length로 확인했습니다. 이렇게 작성하면 행마다 열의 개수가 다른 배열도 안전하게 순회할 수 있습니다.

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

자바의 2차원 배열은 실제로 배열 안에 배열이 들어 있는 구조입니다. 그래서 각 행의 길이가 서로 달라도 됩니다.


실수하기 쉬운 부분

인덱스는 0부터 시작합니다

길이가 5인 배열의 마지막 인덱스는 5가 아니라 4입니다. 반복 조건은 보통 i <= array.length가 아니라 i < array.length로 작성합니다.

length에는 괄호가 없습니다

배열의 길이는 메서드가 아니라 속성처럼 사용합니다.

numbers.length   // 올바른 사용
numbers.length() // 컴파일 오류

배열의 타입은 섞을 수 없습니다

int[]에는 정수만 저장할 수 있습니다. 문자열이나 실수를 넣으면 컴파일 오류가 발생합니다.

int[] numbers = {1, 2, 3};
// numbers[0] = "하나"; // 컴파일 오류

향상된 for문에는 인덱스가 없습니다

값만 필요할 때는 편리하지만, 위치를 알아야 하거나 배열 요소를 직접 교체해야 한다면 일반 for문을 사용합니다.


정리

배열은 같은 타입의 값을 하나로 묶어 관리합니다. 각 요소에는 0부터 시작하는 인덱스로 접근합니다.

  • new int[5]는 정수 다섯 개를 담을 배열을 만듭니다.
  • 배열 길이는 array.length로 확인합니다.
  • 전체 순회에는 일반 for문이나 향상된 for문을 사용합니다.
  • 2차원 배열은 array[row][column] 형태로 접근합니다.
  • 중첩 반복문을 사용하면 2차원 배열 전체를 순회할 수 있습니다.

배열은 앞으로 메서드, 정렬, 컬렉션을 배울 때도 계속 등장합니다. 직접 배열의 값을 바꾸고 반복문으로 출력해보면 인덱스와 length의 관계를 훨씬 빠르게 익힐 수 있습니다.