들어가기 전에

배열에 대해서 알아보기 전에 배열이 왜 필요한지에 대해 먼저 알아보도록 하겠습니다. 이해를 돕기 위해서 가정을 하나 들겠습니다. 우리는 사과 박스 20개를 앞에 두고 각 박스에 있는 사과의 개수를 구한 다음 사과가 총 몇 개나 있는지 알아보고 싶습니다. 각 박스에 있는 사과의 개수를 변수로 나타낸 뒤 총 합을 구하려면 아래와 같을 것입니다.

벌써부터 정신이 아득해질 것 같습니다. 코드 작성을 넘어서 진정한 문서 타이핑이 아닐 수 없습니다. 만약 박스의 개수가 20개에 그치지 않고 100개라고 한다면 어떻게 할까요? 1000개는요? 아마 그날은 변수 선언으로 밤을 지새워야 할지도 모릅니다. 변수를 선언하는 더욱 효율적인 방법이 있으면 좋겠는데 말이죠. 몇 개가 되었든 간에 한꺼번에 선언할 수 있고, 손쉽게 각 변수에 접근할 수 있으면 얼마나 좋을까요?

배열(Array)

배열이란 개념이 이를 해결합니다. 많은 수의 변수를 선언할 필요가 있을 때 배열을 사용하면 이를 간단히 표현할 수 있습니다. 위에서 본 코드를 배열을 이용해 바꿔보도록 하겠습니다.

class ArrayExamples {
	public static void main(String[] args) {
		int[] apple = {20, 27, 24, 26, 32, 40, 39, 27, 28, 23,
				31, 34, 33, 37, 38, 37, 36, 35, 34, 33};
		int sum = 0;
		
		for (int i = 0; i < apple.length; i++) {
			System.out.println((i + 1) + "번째 사과박스에 들어있는 사과의 개수: " + apple[i] + "개");
			sum += apple[i];
		}
		
		System.out.println("총 사과의 개수: " + sum + "개");
	}
}

정말 간단하네요. 코드를 보시면 3행에서 int형 배열을 선언함과 동시에 배열을 초기화하고 있습니다. 그리고 7~10행에선 반복문을 사용해서 인덱스(index)로 각각의 요소에 접근하는 것을 볼 수 있습니다. 여기서 apple.length는 배열 apple의 길이를 말하는 것으로 20이 되겠습니다. 그리고 인덱스는 0부터 시작하므로 apple[0]이 배열 apple의 첫 번째 요소입니다. 예를 들어서, apple[0]은 20이고 apple[1]은 27, apple[2]는 24가 됩니다.

인덱스(index)

색인이라고도 부르며 첨자(subscript)라고 부르기도 합니다. 인덱스는 배열 내에 특정 요소를 가리키는 위치라고 할 수 있으며, 1이 아니라 0부터 시작합니다.

배열의 선언과 생성

자바에서 배열을 사용하려면 아래와 같이 배열을 참조할 참조형 변수를 만들고 어떤 타입의 배열인지 적어줘야 합니다.

요소타입[] 참조변수명; // 요소 타입은 배열 각 요소의 타입을 말합니다.
요소타입 참조변수명[]; // 이렇게도 선언할 수 있지만 잘 사용하지 않습니다.

하지만 참조형 변수는 아직 아무것도 가리키지 않으므로 배열을 만들고 나서 해당 배열을 가리키도록 만들어야 합니다. 참고로 배열의 크기는 0부터 시작할 수 있습니다.

참조변수명 = new 요소타입[배열크기];

객체를 생성하는 방법과 동일해보이는 이유는 자바에선 사실 배열도 객체이기 때문입니다. new 키워드를 사용하여 새로운 배열을 만들고, 만들어진 배열의 참조를 참조형 변수에 대입한 것입니다. 물론 하나로 합쳐서 아래와 같이 적을 수도 있습니다.

요소타입[] 참조변수명 = new 요소타입[배열크기];

예를 들면 아래와 같이 크기가 20인 int형 배열을 만들 수 있습니다.

int[] nums = new int[20];

그리고 배열을 만들면 각 요소는 타입에 따라 기본값으로 초기화됩니다.

배열의 각 요소에 접근할 때는 아래와 같이 인덱스를 통해 접근합니다.

참조변수명[인덱스];

예를 들어서, 배열 nums의 첫 번째 요소에 접근하고자 할 때 nums[0]과 같이 쓸 수 있습니다. (엄밀히 말하면 nums는 배열이 아니라 배열을 가리키는 참조형 변수지만 설명이 길어지므로 편의를 위해 간단하게 배열이라 부르겠습니다.)

우리가 사과 관련 예제 코드에서 봤던 것처럼 아래와 같이 배열을 만들 수도 있습니다. 자바에서는 아래와 같이 배열의 선언, 생성, 초기화를 한 번에 할 수 있는 구문을 제공합니다. 이는 배열 이니셜라이저(array initializer) 구문이라고 하며, 배열을 선언할 때만 사용할 수 있습니다.

요소타입[] 참조변수명 = {값1, 값2, ..., 값n};

예를 들면 아래와 같이 배열을 만들 수 있습니다.

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

하지만 아래와 같이 분리하면 아래와 같은 에러가 발생합니다. 배열 이니셜라이저는 배열을 선언할 때만 사용할 수 있기 때문입니다.

int[] nums;
// 에러: 배열 상수({1, 2, ...})는 이니셜라이저에서만 사용할 수 있습니다.
nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

대신에 아래와 같은 방식으로 시도할 수 있습니다.

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

배열 범위를 초과하지 않도록 조심하자

먼저 아래의 예제 코드를 컴파일 후 실행해보시기 바랍니다.

class ArrayExamples {
	public static void main(String[] args) {
		int[] nums = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
		int sum = 0;
		
		for (int i = 1; i <= nums.length; i++) {
			sum += nums[i];
		}
		
		System.out.println(sum);
	}
}

그러면 아래와 같은 예외가 발생하는 것을 볼 수 있습니다.

위의 예외는 인덱스가 배열의 범위를 초과했기 때문에 발생한 것입니다. 인덱스는 항상 0부터 시작한다는 것을 기억해야 합니다. 위의 코드에서는 nums.length이 10이므로 i가 10이 될 수 있습니다. nums[10]은 11번째 요소를 나타내는데, 배열 nums은 10번째 요소까지만 있기 때문에 배열의 범위를 초과하게 됩니다. 6~7행을 정상적으로 고치면 아래와 같습니다.

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

개선된 for문(enhanced for loop)

자바에선 인덱스를 사용하지 않고 순차적으로 요소에 접근할 수 있는 개선된 for문을 제공합니다. 이를 Foreach 루프라고도 부릅니다. 개선된 for문은 아래와 같이 작성할 수 있습니다. 요소 변수는 각 요소의 값이 임시적으로 담길 변수를 말합니다. 주의할 점으로, 요소 변수를 수정해도 배열의 요소는 변경되지 않습니다.

for (요소타입 요소변수 : 배열) {
	// ...
}

예를 들어서, 기존의 for문을 아래와 같이 바꿔서 작성할 수 있습니다. 아래의 두 코드는 동일한 기능을 합니다.

// 기존의 for문
for (int i = 0; i < nums.length; i++) {
	int num = nums[i];
    sum += num;
}

// 개선된 for문
for (int num : nums) {
    sum += num;
}

큰 장점은 가독성이 올라가며 간단하다는 것이지만 단점으로는 유연성이 떨어집니다. 예를 들어서, 개선된 for문은 역순으로 요소에 접근할 수 없으며, 위에서 말했듯이 요소의 내용도 변경할 수 없습니다. 인덱스로 접근하는 것이 아니기에 인덱스를 이용하여 특정 요소에 접근한다던가, 홀수 번째나 짝수 번째 요소만 확인하는 등의 작업은 할 수 없습니다.