자바에서 배열은 동일한 데이터 타입의 여러 값을 하나의 변수로 관리할 수 있게 해주는 핵심적인 자료구조입니다. 배열을 이해하면 데이터 처리 능력이 크게 향상되며, 컬렉션 프레임워크를 학습하는 데도 기반이 됩니다.
배열의 정의와 주요 특징
배열은 동일한 타입의 요소를 순차적으로 저장하는 고정 크기의 자료구조입니다. 다음 세 가지 특성을 가집니다:
- 동일 타입 제약: 모든 요소는 같은 데이터 타입이어야 합니다.
- 순차적 인덱스: 각 요소는 0부터 시작하는 정수 인덱스로 접근됩니다.
- 길이 불변성: 생성 후 크기를 변경할 수 없습니다.
배열 선언과 초기화 방법
배열은 다양한 방식으로 생성할 수 있습니다:
방법 1: 선언 후 할당
String[] names;
names = new String[3];
방법 2: 동시에 선언 및 생성
double[] scores = new double[5];
방법 3: 리터럴로 초기화
char[] vowels = new char[]{'a', 'e', 'i', 'o', 'u'};
방법 4: 축약형 리터럴 (가장 일반적)
boolean[] flags = {true, false, true};
기본값 자동 초기화
명시적 초기화 없이 배열을 생성하면, 요소들은 자동으로 기본값으로 설정됩니다:
- 정수형 (int, long 등):
0 - 실수형 (float, double):
0.0 - char:
'\u0000' - boolean:
false - 참조형 (String, Object 등):
null
요소 접근과 수정
인덱스를 사용해 특정 위치의 값을 읽거나 변경할 수 있습니다:
int[] data = {100, 200, 300};
int first = data[0]; // 값 읽기
data[1] = 999; // 값 수정
배열 길이 확인
length 필드를 통해 배열의 크기를 얻을 수 있습니다:
int size = data.length; // 결과: 3
배열 반복 처리
모든 요소를 순회하려면 다음과 같은 방법을 사용합니다:
전통적인 for 문
for (int i = 0; i < data.length; i++) {
System.out.println(data[i]);
}
향상된 for 문 (enhanced for)
for (int value : data) {
System.out.println(value);
}
while 문 활용
int idx = 0;
while (idx < data.length) {
System.out.println(data[idx]);
idx++;
}
메서드와 배열
배열은 매개변수나 반환값으로 전달될 수 있습니다. 다음 예제는 배열을 받아 복사본을 역순으로 만드는 메서드입니다:
public static int[] createReverseCopy(int[] source) {
if (source == null) return null;
int[] result = new int[source.length];
for (int i = 0; i < source.length; i++) {
result[i] = source[source.length - 1 - i];
}
return result;
}
public static void displayArray(int[] arr) {
for (int val : arr) {
System.out.print(val + " ");
}
System.out.println();
}
// 사용 예
int[] original = {1, 2, 3};
int[] reversed = createReverseCopy(original);
displayArray(reversed); // 출력: 3 2 1
메모리 내 배열 저장 구조
자바에서 배열은 참조형 데이터이므로 메모리에 다음과 같이 저장됩니다:
- 스택(stack): 배열 변수는 스택에 저장되며, 실제 객체의 메모리 주소(참조)를 포함합니다.
- 힙(heap): 배열 객체 자체와 그 요소들은 힙 영역에 할당됩니다.
예를 들어, 아래 코드를 살펴보겠습니다:
int[] a = {10, 20};
int[] b = {30, 40};
a = b;
이 경우, a는 원래의 {10, 20} 배열을 참조하다가 b가 가리키는 {30, 40}로 참조를 변경합니다. 두 변수 모두 동일한 힙 객체를 가리키게 됩니다.
참조형 요소를 포함한 배열
배열 요소가 객체일 경우, 배열은 각 객체의 참조만 저장합니다. 다음 예제를 보세요:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void introduce() {
System.out.println("안녕하세요, 저는 " + name + "이고, 나이는 " + age + "세입니다.");
}
}
// 배열 생성 및 초기화
Person[] team = new Person[2];
team[0] = new Person("김철수", 25);
team[1] = new Person("이영희", 28);
// 호출
for (Person member : team) {
member.introduce();
}
여기서 team 배열은 힙에 존재하며, 각 요소는 별도로 생성된 Person 객체를 가리키는 참조값을 저장합니다.
자주 묻는 질문
- Q:
System.out.println(arr)를 하면 왜 이상한 문자열이 출력되나요? - A: 배열은 객체이므로, toString()이 오버라이딩되지 않으면 클래스명과 해시코드가 출력됩니다. 내용을 보려면 반복문이나
Arrays.toString(arr)을 사용해야 합니다. - Q: 배열 크기를 나중에 늘릴 수 있나요?
- A: 불가능합니다. 크기가 유동적으로 변해야 한다면
ArrayList를 사용하세요. - Q: 배열 요소는 스택에 저장되나요?
- A: 아닙니다. 배열 객체 전체가 힙에 저장되며, 스택에는 단지 참조만 있습니다.
- Q: 왜 인덱스가 0부터 시작하나요?
- A: 메모리 주소 계산 최적화 때문입니다. 실제 주소 = 기준주소 + (인덱스 × 데이터크기) 공식을 간단히 만들 수 있습니다.
기본형 vs 참조형 비교
| 특징 | 기본형 (Primitive) | 참조형 (Reference) |
|---|---|---|
| 저장 위치 | 스택 | 힙 (데이터), 스택 (참조) |
| 저장 내용 | 실제 값 | 힙 주소 |
| 값 전달 방식 | 값 복사 | 참조 복사 |