배열이란?
배열(Array)은 같은 타입의 데이터를 하나의 변수로 묶어 관리할 수 있게 해주는 자료구조입니다.
예를 들어, 학생 점수 5개를 저장한다고 했을 때,
int score1 = 90;
int score2 = 80;
int score3 = 70;
int score4 = 85;
int score5 = 100;
이렇게 일일이 변수를 만드는 대신, 배열을 사용하면 한 줄로 해결할 수 있어요
int[] scores = {90, 80, 70, 85, 100};
📌 배열 선언과 생성
배열은 선언 → 생성 → 초기화 단계로 구성됩니다.
int[] numbers; // 선언
numbers = new int[5]; // 생성 (기본값 0으로 초기화됨)
또는 한 줄로 합쳐서 작성할 수도 있어요:
int[] numbers = new int[5];
📌 배열 초기화 방법
선언과 동시에 값 설정:
int[] nums = {1, 2, 3, 4, 5};
또는 new 키워드를 사용할 경우:
int[] nums = new int[] {1, 2, 3, 4, 5};
💡배열 크기는 한 번 정하면 변경할 수 없어요!
📌 배열 값에 접근하기
배열의 각 요소는 0번 인덱스부터 시작합니다.
String[] colors = {"Red", "Green", "Blue"};
System.out.println(colors[0]); // Red
System.out.println(colors[1]); // Green
System.out.println(colors[2]); // Blue
⚠️ 존재하지 않는 인덱스에 접근하면 ArrayIndexOutOfBoundsException 예외가 발생해요.
📌 배열과 반복문
일반 for문
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
향상된 for문: for-each
for (int score : scores) {
System.out.println(score);
}
향상된 for문은 읽기 전용이며, 인덱스 접근이 필요할 땐 일반 for문이 더 적합해요.
📌 다차원 배열
배열 안에 배열이 있는 구조예요. 보통 표 형태의 데이터에 사용됩니다.
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
System.out.println(matrix[1][2]); // 6
🔸 가변형 2차원 배열
int[][] arr = new int[2][];
arr[0] = new int[3];
arr[1] = new int[5];
→ 각 행의 길이가 달라도 되는 형태를 만들어 메모리 최적화에 유리할 수 있습니다.
배열은 객체다
📌 배열의 메모리 구조: 스택과 힙
int[] nums = new int[3];
이 코드를 실행하면:
- nums 변수는 스택(stack) 메모리에 위치하고,
- new int[3]은 힙(heap) 메모리에 생성돼요.
- nums는 힙에 있는 배열의 참조 주소(Reference)를 저장하는 거예요.
→ 즉, Java 배열은 객체로 취급되며, 항상 참조형입니다.
📌 .length vs .length()
배열은 사실상 Object를 상속받은 객체입니다.
int[] arr = {1, 2, 3};
System.out.println(arr instanceof Object); // true
하지만 특이하게도 배열의 길이는 메서드가 아닌 속성으로 제공돼요.
String s = "Hello";
System.out.println(s.length()); // ✔ 메서드
int[] arr = {1, 2, 3};
System.out.println(arr.length); // ✔ 속성 (필드), 괄호 없이 사용
- String.length() → 메서드
- array.length → 속성(필드)
즉, 배열의 .length는 일반적인 객체처럼 length() 메서드로 제공되는 게 아니라, 자바가 특별히 배열에 한정해서 제공하는 읽기 전용 필드예요.
📌 얕은 복사(Shallow Copy) vs 깊은 복사 (Deep Copy)
🔸 얕은 복사: 주소만 복사됨
int[] a = {1, 2, 3};
int[] b = a;
b[0] = 100;
System.out.println(a[0]); // 100
→ a와 b는 같은 배열을 가리키므로, 하나가 바뀌면 둘 다 바뀜
🔸 깊은 복사: 실제 값을 복제함
int[] a = {1, 2, 3};
int[] b = a.clone(); // 또는 Arrays.copyOf(a, a.length)
b[0] = 100;
System.out.println(a[0]); // 1
→ 완전히 새로운 배열이 만들어져서 영향을 주지 않음
배열의 특징과 한계
장점
- 빠른 인덱스 접근 (O(1))
- 타입이 고정되어 있어 메모리 효율적
단점
- 크기 변경이 불가능
- 삽입/삭제가 불편
- 중간 요소를 추가하려면 직접 배열 복사 필요
→ 이런 단점 때문에 실무에서는 ArrayList, LinkedList 등 컬렉션(Collection)을 더 자주 사용해요.
그럼에도 불구하고 배열이 여전히 사용되는 이유:
- 성능이 중요한 경우:
배열은 메모리상에 연속적으로 저장되어 있어 인덱스 접근(O(1))이 빠르고, 캐시 적중률도 높아 실시간 처리나 수치 연산 등에서 유리해요. - 기본형(primitive type) 데이터 처리:
컬렉션은 int, double 같은 기본형을 직접 담을 수 없어 Integer, Double 등의 래퍼 클래스로 자동 박싱이 필요해요. 박싱은 성능 비용이 존재하고, GC 대상 객체도 많아져요. 기본형 데이터를 대량으로 다룰 땐 배열이 더 빠르고 메모리도 적게 써요. - 크기가 고정된 데이터 구조에 적합:
배열은 구조가 단순하고, 초기 크기가 명확할 때 더 직관적으로 사용할 수 있어요. - 시스템 연동이나 성능 중심의 저수준 처리:
이미지, 사운드, 네트워크 버퍼 등 시스템 수준의 프로그래밍에서는 배열이 더 적합한 경우가 많아요.
→ 즉, 배열은 기능적으로는 제한적이지만, 단순하고 예측 가능한 구조 덕분에 여전히 가볍고 빠른 처리가 필요한 상황에서 가장 적합한 선택지가 됩니다. 실무에서는 데이터의 성격과 요구 사항에 따라 배열과 컬렉션을 적절히 구분해 사용하는 것이 중요해요.