//배열 기본 구조
// 배열 선언
int[] numbers; // 선언만 하기
int[] numbers = new int[5]; // 선언과 초기화
int[] numbers = {1, 2, 3, 4, 5}; // 선언과 동시에 값 할당
// 배열 사용
numbers[0] = 10; // 값 할당
int value = numbers[0]; // 값 접근
int length = numbers.length; // 배열 길이
2차원 배열:
// 배열 선언
int[][] matrix; // 선언만 하기
int[][] matrix = new int[3][4]; // 3행 4열 배열 생성
int[][] matrix = { // 선언과 동시에 값 할당
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 배열 사용
matrix[0][0] = 1; // 값 할당
int value = matrix[0][0]; // 값 접근
배열의 개념과 동작 방식
배열은 동일한 타입의 여러 데이터를 연속된 메모리 공간에 저장하는 자료구조다.
선언 시 크기가 고정되며, 인덱스를 통해 각 요소에 빠르게 접근할 수 있다.
JVM은 배열 생성 시 힙 메모리에 연속된 공간을 할당하고, 배열 변수는 이 메모리의 시작 주소를 참조한다.
쉬운 설명과 비유
배열은 마치 번호가 매겨진 사물함과 같다. 각 사물함은 같은 크기이고(동일 타입), 번호순서대로 붙어있으며(연속된 메모리), 한 번 만들면 사물함의 개수를 바꿀 수 없다.(고정 크기).
배열의 주요 역할
- 데이터의 순차적 저장
- 인덱스를 통한 빠른 접근
- 동일 타입 데이터의 그룹화
- 반복 작업의 효율적 처리
사용 이유와 목적
- 다수의 변수를 효율적으로 관리
- 데이터 처리의 일관성 확보
- 메모리의 효율적 사용
- 알고리즘 구현의 용이성
자주하는 실수
- ArrayIndexOutOfBoundsException (인덱스 범위 초과)
- 배열 크기를 동적으로 변경하려는 시도
- 배열 초기화 없이 사용
- 참조형 배열에서 객체 생성 누락
배열형 데이터 목록
⭐ : 사용 빈도
- 기본 배열
- 일반 배열 (Array)⭐
- 다차원 배열 (Multi-dimensional Array)⭐
- List 인터페이스 구현체
- ArrayList⭐⭐
- Vector (쓰레드 안전)
- LinkedList⭐
- Stack (Vector 상속)
- Queue 인터페이스 구현체
- PriorityQueue⭐
- ArrayDeque
- LinkedList (Queue 구현)
- Set 인터페이스 구현체 (순서 없는 컬렉션)
- HashSet⭐
- TreeSet
- LinkedHashSet
- Map 인터페이스 구현체 (키-값 쌍)
- HashMap⭐
- TreeMap⭐
- LinkedHashMap⭐
- Hashtable (쓰레드 안전)
- 특수 목적 배열
- Arrays 클래스의 유틸리티
- Collections 클래스의 유틸리티
- StringBuffer/StringBuilder (문자 배열)
- BitSet (비트 배열)
- 동기화된(Thread-safe) 컬렉션
- CopyOnWriteArrayList
- ConcurrentHashMap⭐
- Vector
- Hashtable
실무 활용 예시
// 1. 데이터 일괄 처리
int[] scores = new int[100];
for(int i = 0; i < scores.length; i++) {
processScore(scores[i]);
}
// 2. 캐시 구현
Object[] cache = new Object[1000];
// 3. 이미지 픽셀 처리
int[][] pixels = new int[width][height];
// 4. 고정 크기 버퍼
byte[] buffer = new byte[8192];
// 5. 테이블 데이터 처리
String[][] tableData = new String[rows][columns];
// 6. 정렬 알고리즘 구현
public void bubbleSort(int[] arr) {
// 정렬 로직
}
// 7. 통계 데이터 처리
double[] measurements = new double[1000];
이러한 배열은 성능이 중요하거나 크기가 고정된 데이터를 다룰 때 특히 유용합니다. 하지만 크기가 동적으로 변해야 하는 경우에는 ArrayList나 다른 컬렉션을 고려해야 합니다.
Java에서 가변 길이 데이터를 다루는 주요 방법들
ArrayList 사용 (가장 일반적인 방법)
// 기본 사용
ArrayList<Integer> list = new ArrayList<>();
list.add(1); // 동적으로 추가
list.add(2);
list.add(3);
// 초기 용량 지정
ArrayList<Integer> list = new ArrayList<>(1000); // 성능 최적화
// 배열을 ArrayList로 변환
Integer[] array = {1, 2, 3};
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(array));
LinkedList 사용
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("첫번째");
linkedList.addFirst("맨앞에 추가");
linkedList.addLast("맨뒤에 추가");
동적 배열 재할당
// 수동으로 배열 크기 조정
public class DynamicArray {
private int[] array;
private int size;
public DynamicArray() {
array = new int[10]; // 초기 크기
size = 0;
}
public void add(int element) {
if (size == array.length) {
// 배열이 가득 차면 크기를 2배로 증가
int[] newArray = new int[array.length * 2];
System.arraycopy(array, 0, newArray, 0, array.length);
array = newArray;
}
array[size++] = element;
}
}
//실제 과정
// 1. 처음 상태 (크기 10)
array = [1][2][3][4][5][6][7][8][9][10]
↑ 가득 참
// 2. 새로운 배열 생성 (크기 20)
newArray = [][][][][][][][][][][][][][][][][][][]
// 3. 기존 데이터 복사
newArray = [1][2][3][4][5][6][7][8][9][10][][][][][][][][][][]
// 4. array 변수가 새로운 배열을 가리키도록 변경
array = newArray
// 5. 기존 배열은 더 이상 참조되지 않아 가비지 컬렉션의 대상이 됨
[1][2][3][4][5][6][7][8][9][10] // 이 메모리는 결국 해제됨
- 새로운 더 큰 배열을 만들고
- 기존 데이터를 복사한 후
- 기존 배열은 버려지고(가비지 컬렉션됨)
- 새로운 배열을 사용하게 된다.
Collection 구현체들의 특징 비교
구현체 | 장점 | 단점 | 사용 시나리오 |
ArrayList | - 빠른 조회 - 순차적 추가/삭제 빠름 |
- 중간 삽입/삭제 느림 | - 데이터 조회가 많은 경우 - 순차적 추가/삭제가 많은 경우 |
LinkedList | - 중간 삽입/삭제 빠름 - 메모리 효율적 |
- 조회 속도 느림 | - 삽입/삭제가 많은 경우 - 메모리가 제한적인 경우 |
Vector | - 스레드 안전 | - 성능 저하 | - 멀티스레드 환경 |
실무에서 자주 사용되는 패턴
// 1. 동적 데이터 수집
public List<String> collectData() {
List<String> data = new ArrayList<>();
while (hasMoreData()) {
data.add(getData());
}
return data;
}
// 2. 초기 크기 예측이 가능한 경우
public List<User> getUsers(int expectedSize) {
List<User> users = new ArrayList<>(expectedSize);
// ... 데이터 추가
return users;
}
// 3. 배열과 리스트 변환
public class DataProcessor {
// 리스트를 배열로 변환
public String[] listToArray(List<String> list) {
return list.toArray(new String[0]);
}
// 배열을 리스트로 변환
public List<String> arrayToList(String[] array) {
return new ArrayList<>(Arrays.asList(array));
}
}
성능 최적화 팁
// 1. 초기 용량 지정으로 재할당 횟수 줄이기
List<Integer> list = new ArrayList<>(10000);
// 2. 정확한 크기를 알 때는 배열 사용
int[] array = new int[exactSize];
// 3. 메모리 최적화
list.trimToSize(); // 사용하지 않는 공간 제거
주의사항
- ArrayList는 내부적으로 배열을 사용하므로 크기가 커질 때 재할당이 발생
- 초기 용량을 너무 크게 잡으면 메모리 낭비
- 멀티스레드 환경에서는 동기화 고려 필요
- 기본 타입의 경우 박싱/언박싱 오버헤드 발생
- 이러한 방법들 중에서 대부분의 경우 ArrayList를 사용하는 것이 가장 좋은 선택
- 특별한 요구사항(예: 많은 삽입/삭제, 스레드 안전성 등)이 있는 경우에만 다른 대안을 고려
'Java > 자바 학습' 카테고리의 다른 글
1. 자바 플랫폼 - 1. JDK, JRE, JVM의 차이 (0) | 2025.05.12 |
---|---|
ArrayList와 LinkedList의 주요 차이점 (0) | 2025.05.10 |
자바 문자열의 포매팅 (0) | 2025.05.09 |
자바의 String은 클래스다. (0) | 2025.05.09 |
문자 자료형과 메서드 (0) | 2025.05.08 |