배열(Array) vs ArrayList 완전 비교
📌 학습 목표
- 배열과 ArrayList의 차이점을 이해한다
- 각각의 특징과 장단점을 이해한다
- 언제 배열을 사용하고, 언제 ArrayList를 사용할지 판단할 수 있다
- 배열과 ArrayList의 사용법을 비교하여 학습한다
1️⃣ 배열과 ArrayList란?
배열 (Array)
배열은 같은 타입의 데이터를 연속된 메모리 공간에 저장하는 자료구조입니다.
// 배열 선언 및 초기화
int[] numbers = new int[5]; // 크기 5인 정수 배열
String[] names = {"홍길동", "김철수"}; // 초기값과 함께 선언
ArrayList
ArrayList는 Java 컬렉션 프레임워크의 List 인터페이스를 구현한 클래스로, 배열을 기반으로 동적으로 크기가 변하는 자료구조입니다.
// ArrayList 선언 및 초기화
import java.util.ArrayList;
ArrayList<Integer> numbers = new ArrayList<>(); // 정수 ArrayList
ArrayList<String> names = new ArrayList<>(); // 문자열 ArrayList
2️⃣ 기본 비교표
| 구분 | 배열 (Array) | ArrayList |
|---|---|---|
| 크기 | 고정 (생성 시 결정) | 동적 (자동 조정) |
| 선언 | 타입[] 변수명 |
ArrayList<타입> 변수명 |
| 생성 | new 타입[크기] |
new ArrayList<>() |
| 길이/크기 | .length |
.size() |
| 요소 접근 | 배열[인덱스] |
.get(인덱스) |
| 요소 변경 | 배열[인덱스] = 값 |
.set(인덱스, 값) |
| 요소 추가 | 불가능 (크기 고정) | .add(값) / .add(인덱스, 값) |
| 요소 삭제 | 불가능 | .remove(인덱스) / .remove(값) |
| 타입 | 기본 타입, 참조 타입 모두 | 참조 타입만 (Wrapper 클래스) |
| 메모리 | 연속된 메모리 | 연속된 메모리 (내부 배열) |
| 성능 | 빠름 (직접 접근) | 약간 느림 (메서드 호출) |
3️⃣ 생성 및 초기화 비교
배열 생성 및 초기화
// 방법 1: 크기만 지정
int[] arr1 = new int[5]; // 기본값으로 초기화 (0, false, null)
// 방법 2: 초기값과 함께 선언
int[] arr2 = {1, 2, 3, 4, 5};
String[] arr3 = {"사과", "바나나", "오렌지"};
// 방법 3: 선언과 생성 분리
int[] arr4;
arr4 = new int[3];
// 방법 4: 선언과 초기화 분리
int[] arr5;
arr5 = new int[]{10, 20, 30};
ArrayList 생성 및 초기화
import java.util.ArrayList;
// 방법 1: 기본 생성
ArrayList<Integer> list1 = new ArrayList<>(); // 초기 용량 10
// 방법 2: 초기 용량 지정
ArrayList<Integer> list2 = new ArrayList<>(20); // 초기 용량 20
// 방법 3: 다른 컬렉션으로부터 생성
ArrayList<Integer> list3 = new ArrayList<>(list1);
// 방법 4: 요소 추가
ArrayList<String> list4 = new ArrayList<>();
list4.add("사과");
list4.add("바나나");
list4.add("오렌지");
4️⃣ 주요 작업 비교
4-1. 요소 접근 (읽기)
배열
int[] numbers = {10, 20, 30, 40, 50};
// 인덱스로 접근
int value = numbers[0]; // 10
int value2 = numbers[2]; // 30
// 반복문으로 접근
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
// 향상된 for문
for (int num : numbers) {
System.out.println(num);
}
ArrayList
import java.util.ArrayList;
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
// 인덱스로 접근
int value = numbers.get(0); // 10
int value2 = numbers.get(2); // 30
// 반복문으로 접근
for (int i = 0; i < numbers.size(); i++) {
System.out.println(numbers.get(i));
}
// 향상된 for문
for (int num : numbers) {
System.out.println(num);
}
4-2. 요소 변경 (수정)
배열
int[] numbers = {10, 20, 30};
// 직접 할당
numbers[0] = 100; // {100, 20, 30}
numbers[1] = 200; // {100, 200, 30}
ArrayList
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
// set 메서드 사용
numbers.set(0, 100); // {100, 20, 30}
numbers.set(1, 200); // {100, 200, 30}
4-3. 요소 추가
배열
// ❌ 배열은 크기가 고정되어 있어 요소를 추가할 수 없음
int[] numbers = new int[5];
numbers[0] = 10;
numbers[1] = 20;
// numbers[5] = 30; // ❌ ArrayIndexOutOfBoundsException
// 배열 크기 증가가 필요한 경우 → 새 배열 생성 필요
int[] numbers = {10, 20, 30};
int[] newNumbers = new int[numbers.length + 1];
for (int i = 0; i < numbers.length; i++) {
newNumbers[i] = numbers[i];
}
newNumbers[numbers.length] = 40; // 요소 추가
numbers = newNumbers; // 참조 변경
ArrayList
import java.util.ArrayList;
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10); // 끝에 추가
numbers.add(20); // 끝에 추가
numbers.add(30); // 끝에 추가
numbers.add(1, 15); // 인덱스 1 위치에 추가 → {10, 15, 20, 30}
// 크기가 자동으로 증가
numbers.add(40); // {10, 15, 20, 30, 40}
4-4. 요소 삭제
배열
// ❌ 배열은 크기가 고정되어 있어 요소를 삭제할 수 없음
int[] numbers = {10, 20, 30, 40, 50};
// 요소를 "삭제"하려면 → 새 배열 생성 필요
int[] newNumbers = new int[numbers.length - 1];
for (int i = 0, j = 0; i < numbers.length; i++) {
if (i != 2) { // 인덱스 2 (30) 제외
newNumbers[j++] = numbers[i];
}
}
numbers = newNumbers; // {10, 20, 40, 50}
ArrayList
import java.util.ArrayList;
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
numbers.remove(2); // 인덱스 2 삭제 → {10, 20, 40, 50}
numbers.remove(Integer.valueOf(20)); // 값 20 삭제 → {10, 40, 50}
4-5. 길이/크기 확인
배열
int[] numbers = {10, 20, 30};
int length = numbers.length; // 3 (필드)
ArrayList
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
int size = numbers.size(); // 3 (메서드)
5️⃣ 상세 비교
5-1. 크기 (Size)
배열: 고정 크기
int[] numbers = new int[5]; // 크기 5로 고정
// ❌ 크기 변경 불가
// numbers.length = 10; // 컴파일 에러 (length는 final)
// 크기를 늘리려면 새 배열 생성 필요
int[] newNumbers = new int[10];
System.arraycopy(numbers, 0, newNumbers, 0, numbers.length);
numbers = newNumbers;
ArrayList: 동적 크기
ArrayList<Integer> numbers = new ArrayList<>(); // 초기 용량 10
// 자동으로 크기 증가
numbers.add(1);
numbers.add(2);
// ... 10개 추가 후에도 자동으로 용량 증가
System.out.println(numbers.size()); // 현재 요소 개수
5-2. 타입 제한
배열: 기본 타입 + 참조 타입
// 기본 타입 배열 가능
int[] numbers = {1, 2, 3};
double[] prices = {1.5, 2.5, 3.5};
boolean[] flags = {true, false};
// 참조 타입 배열도 가능
String[] names = {"홍길동", "김철수"};
Integer[] integers = {1, 2, 3};
ArrayList: 참조 타입만 (Wrapper 클래스 사용)
// ❌ 기본 타입 불가
// ArrayList<int> numbers = new ArrayList<>(); // 컴파일 에러
// ✅ Wrapper 클래스 사용
ArrayList<Integer> numbers = new ArrayList<>();
ArrayList<Double> prices = new ArrayList<>();
ArrayList<Boolean> flags = new ArrayList<>();
// 참조 타입
ArrayList<String> names = new ArrayList<>();
5-3. 메모리 관리
배열
// 배열은 연속된 메모리에 저장
int[] arr = new int[1000]; // 1000 * 4바이트 = 4000바이트 연속 메모리 할당
// 크기가 고정되어 있어 메모리 낭비 가능
int[] arr = new int[100]; // 10개만 사용해도 100개 공간 할당
ArrayList
// ArrayList도 내부적으로 배열 사용
ArrayList<Integer> list = new ArrayList<>(); // 초기 용량 10
// 요소가 추가되면 내부 배열 크기 자동 증가 (약 1.5배)
// 메모리 효율적 (필요할 때만 증가)
5-4. 성능 비교
| 작업 | 배열 | ArrayList |
|---|---|---|
| 인덱스 접근 | O(1) - 매우 빠름 | O(1) - 빠름 (메서드 호출 오버헤드) |
| 끝에 추가 | 불가능 | O(1) 평균, O(n) 최악 (크기 증가 시) |
| 중간 삽입 | 불가능 | O(n) |
| 중간 삭제 | 불가능 | O(n) |
| 검색 | O(n) | O(n) |
6️⃣ 실전 예제 비교
예제 1: 점수 관리
배열로 구현
public class ScoreArray {
public static void main(String[] args) {
// 배열: 크기가 고정
int[] scores = new int[5];
scores[0] = 85;
scores[1] = 90;
scores[2] = 78;
scores[3] = 92;
scores[4] = 88;
// 점수 출력
for (int i = 0; i < scores.length; i++) {
System.out.println((i + 1) + "번 학생: " + scores[i] + "점");
}
// 평균 계산
int sum = 0;
for (int score : scores) {
sum += score;
}
double average = (double) sum / scores.length;
System.out.println("평균: " + average);
// ❌ 새로운 학생 추가 불가 (배열 크기 고정)
// scores[5] = 95; // 오류!
}
}
ArrayList로 구현
import java.util.ArrayList;
public class ScoreArrayList {
public static void main(String[] args) {
// ArrayList: 동적 크기
ArrayList<Integer> scores = new ArrayList<>();
scores.add(85);
scores.add(90);
scores.add(78);
scores.add(92);
scores.add(88);
// 점수 출력
for (int i = 0; i < scores.size(); i++) {
System.out.println((i + 1) + "번 학생: " + scores.get(i) + "점");
}
// 평균 계산
int sum = 0;
for (int score : scores) {
sum += score;
}
double average = (double) sum / scores.size();
System.out.println("평균: " + average);
// ✅ 새로운 학생 추가 가능
scores.add(95); // 자동으로 크기 증가
System.out.println("6번 학생 추가: " + scores.get(5) + "점");
// ✅ 특정 학생 제거 가능
scores.remove(2); // 3번 학생 제거
}
}
예제 2: 이름 목록 관리
배열로 구현
public class NameArray {
public static void main(String[] args) {
String[] names = {"홍길동", "김철수", "이영희"};
// 이름 출력
for (String name : names) {
System.out.println(name);
}
// 특정 이름 검색
String target = "김철수";
boolean found = false;
for (String name : names) {
if (name.equals(target)) {
found = true;
break;
}
}
System.out.println(target + " 찾음: " + found);
// ❌ 이름 추가/삭제 불가 (새 배열 생성 필요)
}
}
ArrayList로 구현
import java.util.ArrayList;
public class NameArrayList {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("홍길동");
names.add("김철수");
names.add("이영희");
// 이름 출력
for (String name : names) {
System.out.println(name);
}
// 특정 이름 검색
String target = "김철수";
boolean found = names.contains(target);
System.out.println(target + " 찾음: " + found);
// 인덱스 찾기
int index = names.indexOf(target);
System.out.println(target + "의 인덱스: " + index);
// ✅ 이름 추가
names.add("박민수"); // 끝에 추가
names.add(1, "최지영"); // 인덱스 1에 추가
// ✅ 이름 삭제
names.remove("김철수"); // 값으로 삭제
names.remove(0); // 인덱스로 삭제
}
}
예제 3: 동적 크기 필요 시
배열의 한계
public class ArrayLimitation {
public static void main(String[] args) {
// 사용자가 몇 개를 입력할지 모름
// 배열은 크기를 미리 정해야 함
int[] numbers = new int[10]; // 최대 10개로 가정
// 실제로는 3개만 입력
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
// 메모리 낭비: 7개 공간이 사용되지 않음
// 11번째 추가하려면?
// ❌ 불가능 - 새 배열 생성 필요
}
}
ArrayList의 장점
import java.util.ArrayList;
import java.util.Scanner;
public class ArrayListAdvantage {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ArrayList<Integer> numbers = new ArrayList<>();
// 사용자가 몇 개를 입력할지 모름 → ArrayList가 적합
System.out.println("숫자를 입력하세요 (종료: -1):");
while (true) {
int input = scanner.nextInt();
if (input == -1) {
break;
}
numbers.add(input); // 자동으로 크기 증가
}
System.out.println("입력된 숫자 개수: " + numbers.size());
System.out.println("숫자들: " + numbers);
// 메모리 효율적: 필요한 만큼만 사용
}
}
7️⃣ 배열과 ArrayList 변환
배열 → ArrayList
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayToArrayList {
public static void main(String[] args) {
// 방법 1: Arrays.asList() + ArrayList 생성자
String[] array = {"사과", "바나나", "오렌지"};
ArrayList<String> list1 = new ArrayList<>(Arrays.asList(array));
// 방법 2: 반복문으로 추가
ArrayList<String> list2 = new ArrayList<>();
for (String item : array) {
list2.add(item);
}
// 방법 3: Arrays.asList() (크기 고정 리스트)
List<String> list3 = Arrays.asList(array);
// list3.add("포도"); // ❌ UnsupportedOperationException (크기 고정)
}
}
ArrayList → 배열
import java.util.ArrayList;
public class ArrayListToArray {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("사과");
list.add("바나나");
list.add("오렌지");
// 방법 1: toArray() 메서드
String[] array1 = list.toArray(new String[list.size()]);
String[] array2 = list.toArray(new String[0]); // 크기 0도 가능
// 방법 2: 반복문으로 복사
String[] array3 = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
array3[i] = list.get(i);
}
}
}
8️⃣ 언제 무엇을 사용할까?
✅ 배열을 사용해야 할 때
크기가 고정되어 있고 변하지 않을 때
// 요일: 7개로 고정 String[] days = {"월", "화", "수", "목", "금", "토", "일"};성능이 매우 중요할 때
// 게임의 픽셀 데이터 등 int[] pixels = new int[1920 * 1080];기본 타입을 사용해야 할 때
// 기본 타입 배열이 더 효율적 int[] numbers = new int[1000];간단한 데이터 구조가 필요할 때
// 단순한 데이터 저장 int[] coordinates = {10, 20};
✅ ArrayList를 사용해야 할 때
크기가 변할 수 있을 때
// 사용자가 추가할 수 있는 목록 ArrayList<String> todoList = new ArrayList<>();요소 추가/삭제가 빈번할 때
// 동적으로 관리되는 목록 ArrayList<Student> students = new ArrayList<>(); students.add(new Student("홍길동")); students.remove(0);컬렉션 프레임워크의 기능이 필요할 때
// contains(), indexOf() 등 편리한 메서드 필요 ArrayList<String> names = new ArrayList<>(); if (names.contains("홍길동")) { }동적 데이터 관리가 중요할 때
// 파일에서 읽어오는 데이터 등 ArrayList<String> lines = new ArrayList<>(); // 파일을 읽어서 lines에 추가
9️⃣ 종합 비교 예제
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayVsArrayList {
public static void main(String[] args) {
System.out.println("=== 배열 vs ArrayList 종합 비교 ===\n");
// ========== 1. 생성 및 초기화 ==========
System.out.println("--- 1. 생성 및 초기화 ---");
// 배열
int[] arr = {10, 20, 30, 40, 50};
System.out.println("배열: " + Arrays.toString(arr));
// ArrayList
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
System.out.println("ArrayList: " + list);
// ========== 2. 길이/크기 ==========
System.out.println("\n--- 2. 길이/크기 ---");
System.out.println("배열 길이: " + arr.length);
System.out.println("ArrayList 크기: " + list.size());
// ========== 3. 요소 접근 ==========
System.out.println("\n--- 3. 요소 접근 ---");
System.out.println("배열[0]: " + arr[0]);
System.out.println("ArrayList.get(0): " + list.get(0));
// ========== 4. 요소 변경 ==========
System.out.println("\n--- 4. 요소 변경 ---");
arr[0] = 100;
list.set(0, 100);
System.out.println("배열[0] = 100: " + Arrays.toString(arr));
System.out.println("ArrayList.set(0, 100): " + list);
// ========== 5. 요소 추가 ==========
System.out.println("\n--- 5. 요소 추가 ---");
// 배열: 불가능
System.out.println("배열: 요소 추가 불가 (크기 고정)");
// ArrayList: 가능
list.add(60);
System.out.println("ArrayList.add(60): " + list);
list.add(2, 25);
System.out.println("ArrayList.add(2, 25): " + list);
// ========== 6. 요소 삭제 ==========
System.out.println("\n--- 6. 요소 삭제 ---");
// 배열: 불가능
System.out.println("배열: 요소 삭제 불가 (크기 고정)");
// ArrayList: 가능
list.remove(2);
System.out.println("ArrayList.remove(2): " + list);
list.remove(Integer.valueOf(60));
System.out.println("ArrayList.remove(60): " + list);
// ========== 7. 검색 ==========
System.out.println("\n--- 7. 검색 ---");
// 배열: 반복문으로 직접 검색
boolean found = false;
for (int num : arr) {
if (num == 30) {
found = true;
break;
}
}
System.out.println("배열에서 30 찾기: " + found);
// ArrayList: contains() 메서드
System.out.println("ArrayList에서 30 찾기: " + list.contains(30));
System.out.println("ArrayList에서 30의 인덱스: " + list.indexOf(30));
}
}
🔟 핵심 정리
배열의 특징
- ✅ 고정 크기: 생성 시 크기 결정, 변경 불가
- ✅ 빠른 접근: 인덱스로 직접 접근 (O(1))
- ✅ 기본 타입 가능: int, double 등 직접 사용
- ❌ 크기 변경 불가: 요소 추가/삭제 불가
- ❌ 메모리 낭비 가능: 사용하지 않는 공간
ArrayList의 특징
- ✅ 동적 크기: 필요에 따라 자동으로 크기 증가
- ✅ 편리한 메서드: add(), remove(), contains() 등
- ✅ 유연성: 요소 추가/삭제 자유롭게
- ❌ Wrapper 클래스: 기본 타입 대신 Integer, Double 등 사용
- ❌ 약간 느림: 메서드 호출 오버헤드
선택 가이드
| 상황 | 권장사항 |
|---|---|
| 크기가 고정되어 있음 | 배열 |
| 크기가 변할 수 있음 | ArrayList |
| 성능이 매우 중요 | 배열 |
| 요소 추가/삭제가 빈번 | ArrayList |
| 기본 타입 사용 | 배열 |
| 컬렉션 기능 필요 | ArrayList |
| 간단한 데이터 구조 | 배열 |
| 동적 데이터 관리 | ArrayList |
1️⃣1️⃣ 메모리 구조 이해
배열의 메모리 구조
int[] numbers = {10, 20, 30};
메모리 구조:
Stack 메모리 Heap 메모리
┌─────────────┐ ┌─────────────────────┐
│ numbers │──────────→│ [0] [1] [2] │
│ (참조변수) │ │ 10 20 30 │
└─────────────┘ │ ↑ │
│ 연속된 메모리 공간 │
└─────────────────────┘특징:
- 배열 요소들이 연속된 메모리 공간에 저장됨
- 인덱스로 직접 접근 가능 (주소 계산:
baseAddress + index * size)
ArrayList의 메모리 구조
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
메모리 구조:
Stack 메모리 Heap 메모리
┌─────────────┐ ┌─────────────────────┐
│ list │──────────→│ ArrayList 객체 │
│ (참조변수) │ │ ┌───────────────┐ │
└─────────────┘ │ │ elementData[] │──→│ [0] [1] [2] ... [9] │
│ │ (Object[]) │ │ 10 20 30 null ...│
│ └───────────────┘ │ ↑ │
│ size = 3 │ 내부 배열 (연속) │
│ capacity = 10 │ │
└─────────────────────┘ └─────────────────────┘특징:
- ArrayList 객체가 내부적으로 배열을 가지고 있음
- 기본 용량(default capacity)은 10
- 요소가 추가되면 내부 배열 크기가 자동으로 증가
1️⃣2️⃣ ArrayList 내부 동작 원리
초기 용량과 크기 증가
ArrayList<Integer> list = new ArrayList<>(); // 초기 용량: 10
초기 상태:
ArrayList 내부
┌─────────────────────────────┐
│ elementData: Object[10] │ (용량: 10)
│ [0] [1] [2] ... [9] │
│ null null null ... null │
│ │
│ size = 0 │ (현재 요소 개수: 0)
│ capacity = 10 │ (용량: 10)
└─────────────────────────────┘요소 추가 과정:
list.add(10); // size: 0 → 1
list.add(20); // size: 1 → 2
// ... 10개 추가 후
list.add(100); // 용량 부족! → 내부 배열 크기 증가
용량 증가:
11번째 요소 추가 시:
1. 새 배열 생성 (크기: 약 1.5배 = 15)
2. 기존 요소 복사
3. 새 요소 추가
이전: [10][20]...[90][null][null]...
size=10, capacity=10
이후: [10][20]...[90][100][null][null][null][null][null]
size=11, capacity=15코드로 확인:
ArrayList<Integer> list = new ArrayList<>();
// 초기 용량 확인
System.out.println("초기 상태");
// 요소 추가하면서 용량 변화 관찰
for (int i = 0; i < 20; i++) {
list.add(i);
// 실제로는 capacity 필드가 private이므로 직접 확인 불가
// 하지만 내부적으로 자동으로 용량이 증가함
}
1️⃣3️⃣ 성능 상세 분석
시간 복잡도 비교
| 작업 | 배열 | ArrayList | 설명 |
|---|---|---|---|
| 인덱스 접근 | O(1) | O(1) | 둘 다 매우 빠름 |
| 끝에 추가 | 불가능 | O(1) 평균 | ArrayList는 평균적으로 빠름 |
| 중간 삽입 | 불가능 | O(n) | 뒤의 요소들을 모두 이동 |
| 중간 삭제 | 불가능 | O(n) | 뒤의 요소들을 모두 이동 |
| 검색 | O(n) | O(n) | 선형 검색 |
실제 성능 테스트 예제
import java.util.ArrayList;
public class PerformanceTest {
public static void main(String[] args) {
final int SIZE = 100000;
// 배열 테스트
long start = System.currentTimeMillis();
int[] arr = new int[SIZE];
for (int i = 0; i < SIZE; i++) {
arr[i] = i; // 직접 접근
}
long arrayTime = System.currentTimeMillis() - start;
// ArrayList 테스트
start = System.currentTimeMillis();
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < SIZE; i++) {
list.add(i); // 메서드 호출
}
long listTime = System.currentTimeMillis() - start;
System.out.println("배열 시간: " + arrayTime + "ms");
System.out.println("ArrayList 시간: " + listTime + "ms");
// 배열이 약간 더 빠를 수 있음 (메서드 호출 오버헤드 없음)
}
}
결과 예상:
- 배열: 약간 빠름 (직접 접근, 오버헤드 없음)
- ArrayList: 약간 느림 (메서드 호출, null 체크 등 오버헤드)
차이는 크지 않지만, 매우 많은 반복에서는 의미가 있을 수 있음
1️⃣4️⃣ 실제 사용 패턴 비교
패턴 1: 고정된 데이터 집합
배열 사용 (적합)
// 요일은 항상 7개
String[] daysOfWeek = {
"월요일", "화요일", "수요일", "목요일",
"금요일", "토요일", "일요일"
};
// 성적 등급 (5개로 고정)
char[] grades = {'A', 'B', 'C', 'D', 'F'};
ArrayList 사용 (과도함)
// ❌ 크기가 고정인데 ArrayList 사용 (불필요)
ArrayList<String> days = new ArrayList<>();
days.add("월요일");
// ... 항상 7개만 사용
패턴 2: 사용자 입력 데이터
배열 사용 (부적합)
// ❌ 사용자가 몇 개를 입력할지 모름
int[] scores = new int[100]; // 최대 100개로 가정
// 실제로는 5개만 입력했을 수도 있음 → 메모리 낭비
// 101번째 입력 시?
// → ArrayIndexOutOfBoundsException 발생!
ArrayList 사용 (적합)
// ✅ 크기가 유동적
ArrayList<Integer> scores = new ArrayList<>();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextInt()) {
scores.add(scanner.nextInt()); // 자동으로 크기 증가
}
// 메모리 효율적: 필요한 만큼만 사용
패턴 3: 동적 목록 관리
배열 사용 (복잡함)
// 학생 목록 관리
Student[] students = new Student[10];
int count = 0;
// 추가
if (count < students.length) {
students[count++] = new Student("홍길동");
} else {
// 새 배열 생성 필요
Student[] newStudents = new Student[students.length * 2];
System.arraycopy(students, 0, newStudents, 0, students.length);
students = newStudents;
students[count++] = new Student("홍길동");
}
// 삭제
// → 복잡한 로직 필요 (요소 이동, null 처리 등)
ArrayList 사용 (간단함)
// ✅ 간단하고 명확
ArrayList<Student> students = new ArrayList<>();
// 추가
students.add(new Student("홍길동"));
// 삭제
students.remove(0); // 또는
students.removeIf(s -> s.getName().equals("홍길동"));
1️⃣5️⃣ 자주 발생하는 오류와 해결
오류 1: ArrayIndexOutOfBoundsException
// ❌ 잘못된 코드
int[] arr = new int[5];
arr[5] = 10; // 인덱스 범위 초과 (0~4만 유효)
// ✅ 올바른 코드
int[] arr = new int[5];
if (index >= 0 && index < arr.length) {
arr[index] = 10;
}
오류 2: ArrayList에서 기본 타입 사용 시도
// ❌ 컴파일 에러
ArrayList<int> numbers = new ArrayList<>();
// ✅ Wrapper 클래스 사용
ArrayList<Integer> numbers = new ArrayList<>();
오류 3: 배열의 length vs ArrayList의 size
int[] arr = {1, 2, 3};
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// ❌ 잘못된 사용
int arrSize = arr.size(); // 컴파일 에러
int listLength = list.length; // 컴파일 에러
// ✅ 올바른 사용
int arrSize = arr.length; // 필드
int listSize = list.size(); // 메서드
오류 4: remove() 메서드 오해
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
// ❌ 인덱스가 아닌 값으로 삭제하려면 Wrapper 클래스 사용
list.remove(10); // 인덱스 10을 삭제하려고 시도 → IndexOutOfBoundsException
// ✅ 올바른 사용
list.remove(0); // 인덱스로 삭제
list.remove(Integer.valueOf(10)); // 값으로 삭제
1️⃣6️⃣ Wrapper 클래스 상세 설명
기본 타입과 Wrapper 클래스
| 기본 타입 | Wrapper 클래스 | 배열 | ArrayList |
|---|---|---|---|
int |
Integer |
✅ 가능 | ✅ Integer 사용 |
double |
Double |
✅ 가능 | ✅ Double 사용 |
boolean |
Boolean |
✅ 가능 | ✅ Boolean 사용 |
char |
Character |
✅ 가능 | ✅ Character 사용 |
byte |
Byte |
✅ 가능 | ✅ Byte 사용 |
short |
Short |
✅ 가능 | ✅ Short 사용 |
long |
Long |
✅ 가능 | ✅ Long 사용 |
float |
Float |
✅ 가능 | ✅ Float 사용 |
Wrapper 클래스 사용 예제
// 배열: 기본 타입 직접 사용
int[] numbers = {1, 2, 3};
double[] prices = {1.5, 2.5, 3.5};
boolean[] flags = {true, false};
// ArrayList: Wrapper 클래스 사용
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1); // 자동 박싱 (Auto-boxing)
numbers.add(2);
numbers.add(3);
ArrayList<Double> prices = new ArrayList<>();
prices.add(1.5); // 자동 박싱
prices.add(2.5);
ArrayList<Boolean> flags = new ArrayList<>();
flags.add(true); // 자동 박싱
flags.add(false);
Auto-boxing과 Auto-unboxing
ArrayList<Integer> list = new ArrayList<>();
// Auto-boxing: 기본 타입 → Wrapper 클래스
list.add(10); // int 10이 자동으로 Integer 10으로 변환
// 내부적으로: list.add(Integer.valueOf(10));
// Auto-unboxing: Wrapper 클래스 → 기본 타입
int value = list.get(0); // Integer가 자동으로 int로 변환
// 내부적으로: int value = list.get(0).intValue();
1️⃣7️⃣ 다차원 배열 vs ArrayList의 중첩
다차원 배열
// 2차원 배열
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 접근
int value = matrix[0][1]; // 2
// 크기 확인
int rows = matrix.length; // 3
int cols = matrix[0].length; // 3
ArrayList 중첩
// ArrayList의 ArrayList
ArrayList<ArrayList<Integer>> matrix = new ArrayList<>();
// 행 추가
ArrayList<Integer> row1 = new ArrayList<>();
row1.add(1);
row1.add(2);
row1.add(3);
matrix.add(row1);
ArrayList<Integer> row2 = new ArrayList<>();
row2.add(4);
row2.add(5);
row2.add(6);
matrix.add(row2);
// 접근
int value = matrix.get(0).get(1); // 2
// 크기 확인
int rows = matrix.size(); // 2
int cols = matrix.get(0).size(); // 3
비교표
| 특징 | 다차원 배열 | ArrayList 중첩 |
|---|---|---|
| 크기 | 고정 (행×열) | 동적 (각 행 크기 다를 수 있음) |
| 접근 | arr[i][j] |
list.get(i).get(j) |
| 행 크기 | 모두 동일 | 다를 수 있음 |
| 유연성 | 낮음 | 높음 |
1️⃣8️⃣ 컬렉션 프레임워크 맥락에서의 ArrayList
컬렉션 프레임워크 계층 구조
Collection 인터페이스
└── List 인터페이스
├── ArrayList (구현 클래스)
├── LinkedList (구현 클래스)
└── Vector (구현 클래스, 구식)List 인터페이스의 주요 메서드
ArrayList는 List 인터페이스를 구현하므로 다음 메서드들을 사용할 수 있습니다:
List<String> list = new ArrayList<>(); // 다형성 활용
// List 인터페이스 메서드
list.add("사과");
list.get(0);
list.set(0, "딸기");
list.remove(0);
list.size();
list.isEmpty();
list.contains("사과");
list.indexOf("사과");
list.clear();
다형성 활용
// List 인터페이스 타입으로 선언
List<String> list1 = new ArrayList<>();
List<String> list2 = new LinkedList<>(); // 나중에 쉽게 변경 가능
// 같은 인터페이스를 구현하므로 사용법 동일
list1.add("사과");
list2.add("사과");
1️⃣9️⃣ 실전 활용 예제
예제 1: 학생 성적 관리 시스템
배열로 구현 (한계)
public class StudentScoreArray {
private int[] scores;
private int count;
public StudentScoreArray(int maxSize) {
scores = new int[maxSize];
count = 0;
}
public void addScore(int score) {
if (count < scores.length) {
scores[count++] = score;
} else {
System.out.println("더 이상 추가할 수 없습니다!");
}
}
// 삭제 기능 복잡...
// 평균 계산 등...
}
ArrayList로 구현 (유연함)
import java.util.ArrayList;
public class StudentScoreArrayList {
private ArrayList<Integer> scores;
public StudentScoreArrayList() {
scores = new ArrayList<>();
}
public void addScore(int score) {
scores.add(score); // 자동으로 크기 증가
}
public void removeScore(int index) {
scores.remove(index); // 간단하게 삭제
}
public double getAverage() {
if (scores.isEmpty()) return 0;
int sum = 0;
for (int score : scores) {
sum += score;
}
return (double) sum / scores.size();
}
public int getMaxScore() {
int max = scores.get(0);
for (int score : scores) {
if (score > max) {
max = score;
}
}
return max;
}
}
예제 2: 파일에서 데이터 읽기
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderExample {
public static void main(String[] args) {
// 파일에서 몇 줄을 읽을지 모름 → ArrayList 적합
ArrayList<String> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line); // 자동으로 크기 증가
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("읽은 줄 수: " + lines.size());
for (String line : lines) {
System.out.println(line);
}
}
}
예제 3: 동적 메뉴 시스템
import java.util.ArrayList;
import java.util.Scanner;
public class MenuSystem {
private ArrayList<String> menuItems;
private Scanner scanner;
public MenuSystem() {
menuItems = new ArrayList<>();
scanner = new Scanner(System.in);
}
public void addMenuItem(String item) {
menuItems.add(item);
}
public void removeMenuItem(int index) {
if (index >= 0 && index < menuItems.size()) {
menuItems.remove(index);
}
}
public void displayMenu() {
System.out.println("=== 메뉴 ===");
for (int i = 0; i < menuItems.size(); i++) {
System.out.println((i + 1) + ". " + menuItems.get(i));
}
}
public int selectMenu() {
displayMenu();
System.out.print("선택: ");
return scanner.nextInt();
}
}
2️⃣0️⃣ 최종 선택 가이드
결정 트리
데이터를 저장해야 함
│
├─ 크기가 고정되어 있나?
│ ├─ 예 → 배열 사용
│ └─ 아니오
│ │
│ ├─ 요소 추가/삭제가 빈번한가?
│ │ ├─ 예 → ArrayList 사용
│ │ └─ 아니오
│ │ │
│ │ ├─ 성능이 매우 중요한가?
│ │ │ ├─ 예 → 배열 고려
│ │ │ └─ 아니오 → ArrayList 사용
│ │
│ └─ 기본 타입을 사용해야 하나?
│ ├─ 예 → 배열 사용
│ └─ 아니오 → ArrayList 사용빠른 참조표
| 상황 | 권장 | 이유 |
|---|---|---|
| 요일 7개 | 배열 | 크기 고정 |
| 사용자 입력 개수 미정 | ArrayList | 동적 크기 |
| 게임 픽셀 데이터 | 배열 | 성능 중요 |
| 학생 목록 관리 | ArrayList | 추가/삭제 빈번 |
| 좌표 (x, y) | 배열 | 간단한 고정 데이터 |
| 파일에서 읽은 라인들 | ArrayList | 개수 미정 |
| 성적 등급 (A~F) | 배열 | 크기 고정 |
| 쇼핑 카트 | ArrayList | 동적 추가/삭제 |
📚 관련 자료
'BackEnd > Java' 카테고리의 다른 글
| 8_3. 도서 관리시스템 (0) | 2026.01.20 |
|---|---|
| 9_1장_도서 대여 시스템 (1) | 2026.01.16 |
| 8_2 .학생 관리 시스템 실습 문제 (0) | 2026.01.15 |
| 8_1. 회차 은행 어플리케이션 프로그램 (0) | 2026.01.14 |
| 5_1 . 이차원 배열 연습문제 (0) | 2026.01.06 |