2025. 4. 23. 17:18ㆍJAVA/JAVA 기초 문법
🖥️ Array(배열)란?
배열은 단일 데이터 타입의 일정한 개수의 값을 보관하는 컨테이너 객체다.
배열이 생성될 때 배열의 길이가 결정된다. 생성 후에는 그 길이가 고정된다.
자바에서 배열은 클래스 객체의 인스턴스 취급 받으며, new 키워드로 생성된다.
int[] anArray = new int[10]; // 자바에서 배열을 만드는 문법
💬 배열은 동일한 데이터 타입의 엘리먼트들을 가지는 데이터 구조
⌨️ 배열의 element란?

배열의 각 아이템을 element라고 하며, 각 엘리먼트는 숫자 인덱스[index]로 엑세스됩니다.
인덱스 넘버링은 0부터 시작된다. 예를 들어, 9번째 엘리먼트틑 인덱스 8에서 엑세스된다.
쉽게 말하면 배열은 일렬로 정렬된 값들의 박스이고, 엘리먼트는 그 박스 안에 담긴 하나하나의 값이다
엘리먼트는 배열을 이루는 핵심 단위이며, 배열에서 엘리먼트 하나하나는 데이터 단위(동일한 데이터 단위)
또한 배열 엘리먼트를 다룰 때 인덱스 관리가 정확하지 않으면 에러가 발생하기 쉽다.

🖱️ element의 특징
| 특징 | 설명 |
| 인덱스 접근 | 엘리먼트틑 [0], [1] 등으로 접근. 인덱스 넘버링은 0부터 시작 |
| 값 저장소 | 엘리먼트는 배열이 저장하는 실제 데이터 |
| 타입 일관성 | 모든 엘리먼트는 배열에 선언된 자료형과 동일한 데이터 타입 |
| 초기값 존재 | 배열 생성 시, 엘리먼트는 자동 초기화됨(기본값) |
| 수정 가능 | 배열[index] = 값 으로 언제든 수정 가능 |
public class ArrayDemo {
public static void main(String[] args) {
int[] anArray = new int[5]; // 각 엘리멘트의 데이터 타입은 int이고
// 엘리먼트 개수가 5개인 배열을 만드는 문법
// 배열의 각각의 엘리먼트는 인덱스가 주어진다
anArray[0] = 100; // 배열의 각 엘리먼트 '값'을 설정하는 문법
anArray[1] = 200;
anArray[2] = 300;
anArray[3] = 400;
anArray[4] = 500;
System.out.println("Element at index 0: "
+ anArray[0]);
System.out.println("Element at index 1: "
+ anArray[1]);
System.out.println("Element at index 2: "
+ anArray[2]);
System.out.println("Element at index 3: "
+ anArray[3]);
System.out.println("Element at index 4: "
+ anArray[4]);
}
💬 각 엘리먼트의 start address 정보를 따로 유지할 필요는 없다.
첫 번째 엘리먼트의 start address만 안다면 offset을 적용할 수 있다
⌨️ 배열의 Offset(오프셋)이란?
배열의 오프셋은 배열의 시작 위치(start address, base address)로부터 특정 요소 까지의 거리(간격)을 의미한다.
즉, 오프셋은 배열[인덱스] 에서 인덱스가 실제 메모리 주소에서 얼마나 떨어져 있는지를 말한다.
자바 배열은 메모리상에 같은 데이터 타입의 엘리먼트들을 연속적으로 배치한다.
각 엘리먼트는 고정된 크기(예 : int = 4byte)를 가지므로, 인덱스를 곱해 오프셋을 계산 할 수 있다.
즉, 첫 엘리먼트의 주소만 알면 전체 배열 엘리먼트의 위치 계산이 가능하다.

🏸 Quiz) 엘리먼트 인덱스가 [3]인 엘리먼트 start address는?
Answer) 0x1000+(4x3) → 0x100C
즉, 배열의 인덱스 = 오프셋 / 엘리먼트 크기
반대로, 오프셋 = 인덱스 x 엘리먼트 크기
🤔 만약 자바에서 배열의 오프셋 개념이 없다면? 🤔
만약 오프셋이 없다면, 배열은 더 이상 "빠른 자료구조"가 아니게 되고,
자바 내부에서도 성능과 메모리 효율이 급격히 나빠지는 결과를 초래하게 된다.
1️⃣ 배열 접근이 O(1) → O(n) 으로 느려짐
// 오프셋이 있다면✔️ :
엘리먼트 주소 = base + index * sizw (빠름) ✔️
// 오프셋이 없다면❌ :
배열[5]에 접근하기 위해 배열[0]부터 차례로 탐색해야 함
- 배열의 가장 큰 장접인 빠른 인덱스 접근이 사라짐
- 리스트와 같은 선형 탐색 구조가 된다
2️⃣ 각 요소의 위치를 별도로 저장해야 함
오프셋이 없다는 건, 각 엘리먼트가 메모리상 어디에 있는지 계산할 수 없다는 뜻이다.
→ 각 엘리먼트의 메모리 주소를 배열처럼 저장해둬야 함/
→ 즉, "포인트 배열" 형태가 되어야 함.
→ 메모리 낭비 + 구조 복잡도 증가
3️⃣ CPU 캐시 효율성(CPU cache locality) ↓ ↓ ↓
오프셋이 있어 배열 엘리먼트가 연속된 메모리 공간에 있을 때,
CPU는 한번에 여러 요소를 캐시라인에 미리 불러와 빠르기 처리한다.
하지만,
❌ 오프셋이 없다면 엘리먼트 간 메모리 위치를 예측할 수 없고
❌ CPU는 캐시 미스를 자주 겪데 된다.
→ 고성능 연산에서 병목 발생
4️⃣ JVM 내부 구현도 훨씬 비효율적으로 변경
지금의 JVM은 배열에 접근할 때 :
배열[5] → base 주소 + (5 x 타입 크기) 직접 계산 후 접근
오프셋이 없어지면 JVM은 :
for(int i = 0; i < index; i++) {
현재 위치 = 다음 요소의 위치를 포인터로 따라가기
}
→ JVM 자체가 훨씬 무겁고 느린 구조가 되버린다
😄 오프셋은 배열이 배열일 수 있게 해주는 이유
오프셋 덕분에 자바의 배열은
- 빠르고 단순한 인덱스 기반 접근이 가능하고
- 메모리를 연속적으로 쓸 수 있어서 공간 효율이 높고
- CPU 캐시 효율도 극대화할 수 있다.
이 구조는 단순히 "기술적으로 괜찮다"가 아니라,
시스템 프로그래밍 기본 원칙이자 고성능 컴퓨터의 핵심 기반이다
⌨️ 배열의 반복 구문(Looping over Arrays)
1️⃣ 전통적인 for 반복문
public class UseArrays {
public static void useArray() {
int[] studentsId = new int[20];
for (int i=0; i<studentsId.length; i++) {
studentsId[i] = i + 1;
}
// for 반목구문
for (int i=0; i<studentsId.length; i++) {
studentsId[i] = studentsId[i] + 1000;
}
}
}
| 장점 | 설명 |
| 인덱스 사용 가능 | i 를 통해 위치 기반 연산 가능 (ex : 짝수 인덱스만 접근) |
| 수정 가능 | students[i] = newValue 처럼 요소 수정 가능 |
2️⃣ 향상된 for-each 반복문
public static void main(String[] args){
Object obj;
int[] numbers = {1,2,3,4,5,6,7,8,9,10};
// 자바 컴파일러가 배열에 대한 for-each 문을 내부적으로
// for (int i = 0; i < arr.length; i++) 형태로 변환
for (int item : numbers) {
System.out.println("Count is: " + item);
}
}
| 장점 | 설명 |
| 코드가 간결함 | 인덱스, 조건 없이 깔끔하게 반복 |
| 일기 전용 | 요소 값만 읽고, 수정은 불가 |
3️⃣ while 반복문
public class WhileDemo {
public static void main(String[] args){
int loopingCount = 1; // for 구문에서 int i = 1;
// while(expression)의 조건식이 true 이면
// while 블럭의 Statement(s)들을 수행한다.
// 언제까지? while(expression)의 조건식이 true이면 계속해서
// while 블럭의 statement(s)들을 수행한다.
while (loopingCount < 11)/* for 구문에서 i<11;*/ {
// true or false
System.out.println("Count is: " + loopingCount);
loopingCount++; // for 구문에서 i++
}
// while 조건식의 값이 false가 되면,
// while 블럭의 next statement로 실행 순서가 이루어진다.
}
}
| 장점 | 설명 |
| 반복 조건을 더 유연하게 제어 가능 | 반복 중 break, continue 사용 유용 |
4️⃣ do-while 반복문
public class DoWhileDemo {
public static void main(String[] args){
int count = 11;
int a = 0;
do {
int doVariable = 0;
System.out.println("Count is: " + count);
count++;
} while (count < 11); // 첫번째 반복시에 while 조건식 값이
// false 이더라도
// 한번은,,, do..while 블럭의 스테이트먼트가 수행됨
System.out.println();
}
}
| 특징 | 설명 |
| 무조건 한 번은 실행됨 | 조건이 나중에 검사되기 때문 |
🤔 만약 배열을 쓰지 않는다면? 🤔
public class WhyUseArray {
// ✅ 배열을 사용하면
public static void useArrayForId() {
int[] studensId = new int[20];
for (int i=0; i<studensId.length; i++) {
studensId[i] = i + 1;
}
for (int i=0; i<studensId.length; i++) {
studensId[i] = studensId[i] + 1000;
}
}
// ❌ 배열이 없다면
public static void main(String ...args) {
int st1Id = 0;
int st2Id = 0;
int st3Id = 0;
int st4Id = 0;
int st5Id = 0;
.
.
.
int st1000Id = 0;
st1Id = 1;
st2Id = 2;
st3Id = 3;
st4Id = 4;
st5Id = 5;
.
.
.
st1000Id = 1000;
st1Id = st1Id + 1000;
st2Id = st2Id + 1000;
st3Id = st3Id + 1000;
st4Id = st4Id + 1000;
st5Id = st5Id + 1000;
.
.
.
st1000Id = st1000Id + 1000;
}
}
배열 없이 데이터를 여러 개 저장하고 반복적으로 처리 하기란 사실상 불가능에 가까움
→ 유지 보수도, 반복문도, 연산도 불가능 ❌
❗❗ Java 배열의 단점 ❗❗
1️⃣ 크기 고정 (Fixed Size)
배열은 한 번 생성하면 크기를 변경할 수 없다.
public class ArrayDemo {
public static void main(String[] args) {
int[] anArray = new int[5]; // 나중에 엘리먼트를 10개로 늘리고 싶을 때?
// 새 배열을 만들어 복사해야 함
anArray[0] = 100;
anArray[1] = 200;
anArray[2] = 300;
anArray[3] = 400;
anArray[4] = 500;
int[] anArray6 = new int[6];
System.arraycopy(anArray, 0, anArray6, 0, 10);
anArray[5] = 600; // 인덱스가 6인 엘리먼트틑 없는데,
// 인덱스가 6인 엘리먼트에 특정 값을 할당하려고 하니,
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:
// Index 5 out of bounds for length 5
// 에러 발생
}
💬 자바 배열은 연속된 메모리 공간을 쓰기 때문에 중간에 늘리거나 줄일 수 없음
실무에서 불편함 : 동적으로 데이터를 추가/삭제하기 어려움
2️⃣ 삽입(insert)/삭제(delete)/꼬리 추가(attach) 불가능 or 비효율적
중간에 데이터를 삽입하거나 삭제할 때 모든 엘리먼트를 직접 이동해야 함
ArrayList는 이런 삽입/삭제/꼬리 추가 작업을 내부적으로 처리해주지만,
배열은 전부 수동으로 조작해야 한다.
▶ 삽입( 예: 인덱스 2번에 값 넣기)
for (int i = arr.length - 1; i > index; i--) {
arr[i] = arr[i - 1]; // 뒤로 한 칸씩 밀기
}
arr[index] = newValue;
→ 수작업 + 공간 부족하면 new 배열을 만들어야 함
▶ 삭제
for (int i = index; i < arr.length - 1; i++) {
arr[i] = arr[i + 1]; // 앞으로 당기기
}
→ 실제로는 삭제된 게 아니라 "마지막 엘리먼트가 중복됨"
▶ 꼬리 추가
배열에 여유 공간이 있을 때만 수동으로 가능
여유 공간이 없으면? → 새 배열을 만들어서 복사해야 함
int[] newArr = new int[arr.length + 1];
System.arraycopy(arr, 0, newArr, 0, arr.length);
newArr[arr.length] = newValue;
3️⃣ 데이터 타입 고정(Single Type Only)
배열은 하나의 타입만 저장 가능
Object[] arr = {1, "Hello", true}; // Object 배열로는 가능하긴 함
하지만 기본적으로 int[], String[] 등 동일 타입만 저장 가능
4️⃣ 타입 제한으로 인한 박식/언박싱 성능 저하
List<Integer> list = new ArrayList<>();
// int[] → Integer[] 변환 시 오토박싱 발생 → 성능 저하 가능
배열은 기본형을 담을 수 있지만, ArrayList는 불가
→ 배열 간 변환 시 박싱 비용 발생
5️⃣ 기능 부족
배열 자체는 메서드를 거의 제공하지 않음
arr.length // O
arr.add(3) // X ❌
arr.remove(2) // X ❌
이런 기능은 전부 java.util.Array 또는 ArrayList 에서 제공
배열은 기본 저장소에 가까움, 로직은 다 수작업
6️⃣ 메모리 낭비 가능성
너무 크게 잡으면 낭비, 작게 잡으면 오버플로우 가능성
int[] arr = new int[1000]; // 100개만 써도 900칸은 낭비
특히 사용량이 예측되지 않는 상황에서 치명적
크기를 유연하게 조절할 수 없는 것이 문제의 핵심
🎯 정리 🎯
✅ 배열은 Java의 기초이자, 모든 자료구조의 출발점
✅ 단순하지만 자주 쓰이고, 성능 최적화에도 큰 영향을 줌
'JAVA > JAVA 기초 문법' 카테고리의 다른 글
| Control Flow Statements (1) | 2025.05.14 |
|---|---|
| Operators (연산자) (0) | 2025.04.30 |
| 자바에서 This (0) | 2025.04.15 |
| Constructor (생성자) (0) | 2025.04.14 |
| Access Modifier(접근 제어자) (0) | 2025.04.04 |