배열은 고정 크기 리스트다
자바의 배열은 같은 타입의 값을 정해진 개수만큼 순서대로 담고, 위치로 접근합니다. 핵심 단어는 "고정"입니다. 배열을 한번 만들면 그 길이는 결코 바뀌지 않습니다. 그리고 "같은 타입": int[]는 int 값만, String[]는 문자열만 담습니다. 이 경직성은 배열이 빠르고 메모리를 적게 차지하는 대가입니다.
배열 타입은 요소 타입 뒤에 []를 붙여 선언하고, 그다음 리터럴을 할당하거나 new로 할당합니다:
대괄호는 타입 뒤(int[] scores)에 둘 수도, 이름 뒤(int scores[])에 둘 수도 있습니다. 둘 다 컴파일되지만, int[] scores가 자바의 관용적인 스타일입니다 - []는 마땅히 있어야 할 자리, 즉 타입 옆에 두세요.
new로 배열 만들기
값은 아직 모르지만 크기는 아는 경우 new를 사용합니다. 이는 칸을 할당하고 그 타입의 기본값으로 채웁니다:
기본값은 무작위 쓰레기 값이 아닙니다 - 자바는 모든 요소를 0으로 초기화합니다. 숫자 타입은 0(또는 0.0)으로, boolean은 false로, String 같은 참조 타입은 null로 시작합니다. 이 null은 기억해 둘 만합니다. 막 할당된 String[]은 null로 가득 차 있고, 그중 하나에 메서드를 호출하면 NullPointerException이 던져집니다.
인덱싱은 0부터 시작한다
요소는 0부터 세는 인덱스로 읽고 씁니다. 마지막 유효 인덱스는 항상 length - 1입니다:
일부 스크립트 언어의 배열과 달리, 범위를 벗어나도 조용히 null이나 undefined를 주지 않습니다 - 예외를 던집니다. 존재하지 않는 인덱스를 읽어 보세요:
이 프로그램은 다음과 함께 멈춥니다:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
이는 초보자가 가장 흔히 저지르는 오류 중 하나입니다. 해결책은 거의 항상 반복문 조건에서의 하나 차이(off-by-one) 실수입니다 - <를 써야 할 곳에 <=를 쓰는 것이죠.
length는 메서드가 아니라 필드다
모든 배열은 자신의 크기를 length 필드에 담고 있습니다. 주의: 괄호가 없습니다.
누구나 적어도 한 번은 걸려드는 함정이 있습니다. 배열은 .length(필드)를 쓰지만 String은 .length()(메서드)를 씁니다. 배열에 nums.length()라고 쓰면 컴파일 오류이고, 문자열에 text.length라고 쓰는 것도 마찬가지입니다. 같은 단어지만 규칙이 다릅니다 - 배열 쪽에는 괄호가 없습니다.
배열을 반복하기
for-each 반복문은 이전 페이지에서 배웠고, 인덱스가 필요 없을 때 모든 요소를 읽는 가장 깔끔한 방법입니다:
인덱스가 필요할 때 - 위치 번호를 출력하거나, 요소를 제자리에서 수정하거나, 거꾸로 순회할 때 - 는 length를 경계로 삼는 고전적인 인덱스 기반 for 반복문을 사용하세요:
조건은 <=가 아니라 i < names.length입니다. <=를 쓰면 한 걸음 더 나아가서 앞 절에서 본 범위 초과 예외를 던집니다. 간단한 규칙: 값만 읽을 때는 for-each, 위치가 필요하거나 배열에 다시 쓰고 싶을 때는 인덱스 기반 for.
다차원 배열
2차원 배열은 사실 배열의 배열입니다 - 행과 열로 이루어진 격자로 생각하세요. 대괄호 한 쌍을 더 추가합니다:
grid[행][열]은 하나의 칸을 고릅니다. 각 행이 자체적인 배열이므로 grid.length는 행의 개수이고 grid[0].length는 첫 번째 행의 열 개수입니다. 행들이 같은 길이일 필요조차 없습니다(이를 들쭉날쭉한 배열, jagged array라고 합니다). 다만 대부분의 격자는 직사각형입니다.
Arrays 유틸리티 클래스
배열 자체에는 메서드가 거의 없습니다 - 직접 정렬하거나 쓸모 있게 출력할 수 없습니다. 자바는 바로 이를 위해 정적 헬퍼로 가득 찬 java.util.Arrays 유틸리티 클래스를 제공합니다:
여기서 기댈 만한 점이 몇 가지 있습니다. Arrays.toString(arr)은 배열을 출력하는 올바른 방법입니다 - 직접 출력하면 [I@1b6d3586 같은 쓸모없는 문자열이 나옵니다. Arrays.sort는 제자리에서 정렬합니다. Arrays.copyOf는 "크기 조정"을 하는 방법입니다: 요청한 길이의 새 배열을 반환하고 기본값으로 채웁니다(위에서 추가된 두 칸은 0). 그리고 Arrays.binarySearch는 이미 정렬된 배열에서만 올바르게 동작합니다.
배열 vs ArrayList
크기를 미리 알고 있고 변하지 않을 때는 일반 배열을 사용하세요 - 고정된 요일 집합, 게임 보드, 픽셀 데이터 같은 것들. 배열은 빠르고 가볍지만, 고정 길이는 진짜 한계입니다: 길이가 6인 배열에 일곱 번째 요소를 추가하려면 완전히 새 배열을 만들어야 합니다.
항목을 자유롭게 추가하고 제거해야 하는 순간, 배열은 거추장스러워집니다. 바로 그때 쓰는 것이 ArrayList입니다 - 복사 작업을 대신 처리해 주는, 크기를 조정할 수 있고 늘어나는 리스트죠.
다음: ArrayList
항목이 몇 개가 될지 모르거나, 프로그램이 실행되는 동안 그 수가 바뀔 때는 고정 길이 배열이 방해가 됩니다. ArrayList가 자바의 답입니다: 인덱스를 일일이 다루는 대신 add와 remove 메서드로, 필요에 따라 늘었다 줄었다 하는 리스트입니다. 다음에 다룰 내용입니다.
자주 묻는 질문
자바에서 배열을 어떻게 선언하고 초기화하나요?
타입을 대괄호와 함께 선언하고 리터럴을 할당합니다: int[] nums = {1, 2, 3};. 고정 크기의 빈 배열을 만들려면 new를 사용합니다: int[] nums = new int[5]; - 이렇게 하면 5개의 칸이 생기고, 각 칸은 기본값으로 채워집니다(int는 0, 객체는 null).
자바에서 배열의 길이는 어떻게 얻나요?
length 필드를 사용합니다(괄호 없이): nums.length. 이것은 메서드가 아니라 필드라서 nums.length()는 컴파일 오류입니다. 참고로 String은 괄호가 있는 .length()를 쓰지만 배열은 괄호가 없는 .length를 씁니다 - 흔히 헷갈리는 부분입니다.
자바 배열은 만든 뒤에 크기를 바꿀 수 있나요?
아니요. 자바 배열은 만들 때 정해진 고정 길이를 가지며, 늘리거나 줄일 수 없습니다. 크기를 바꿀 수 있는 리스트가 필요하다면 ArrayList를 사용하세요. 배열을 "크기 조정"하려면 더 큰 새 배열을 만들어 요소를 복사해야 합니다.
자바에서 ArrayIndexOutOfBoundsException은 무엇인가요?
존재하지 않는 인덱스 - 0보다 작거나 >= array.length - 에 접근할 때 발생하는 런타임 오류입니다. 일부 언어와 달리 자바는 범위를 벗어난 접근에 대해 기본값이나 null을 반환하지 않고 예외를 던집니다. 유효한 인덱스는 항상 0부터 length - 1까지입니다.