자바에서 다수의 데이터를 다룰 때, 가장 먼저 떠오르는 자료구조는 배열(Array)이다.
하지만 실무에서는 배열보다 컬렉션 프레임워크(Collection Framework)를 압도적으로 많이 사용한다.
컬렉션이 무엇인지, 그리고 왜 배열을 대체하게 되었는지 기술적인 관점에서 알아본다.
1. 컬렉션(Collection)이란 무엇인가?
사전적으로는 ‘데이터(객체)들의 모음’을 의미한다. 프로그래밍에서 컬렉션 프레임워크란 다수의 데이터를 쉽고 효과적으로 관리할 수 있도록 자바가 공식적으로 제공하는 표준화된 클래스와 인터페이스의 집합이다.
데이터를 저장하고 관리하는 아키텍처에 따라 크게 List, Set, Map이라는 세 가지 핵심 인터페이스로 분류되며, 개발자는 데이터의 성격(순서 보장 여부, 중복 허용 여부 등)에 맞춰 적절한 구현체(ArrayList, HashSet, HashMap 등)를 골라 쓰기만 하면 된다.
2. 왜 배열(Array) 대신 컬렉션을 사용할까?
배열은 메모리에 연속적으로 할당되어 조회 속도가 빠르다는 장점이 있지만, 구조적으로 큰 한계를 지니고 있다.
① 메모리 크기의 유연성 (고정 크기 vs 동적 크기)
배열
- 메모리를 연속적으로 할당받기 위해 처음 생성할 때 크기를 미리 지정해야 하며, 한 번 정해진 크기는 실행 도중 절대 변경할 수 없다.
- 지정한 크기를 초과하여 데이터를 넣으려 하면 ArrayIndexOutOfBoundsException 에러가 발생하며 프로그램이 강제 종료된다.
- 더 큰 배열이 필요해지면 새로운 배열을 메모리에 다시 할당하고, 기존 데이터를 일일이 복사하는 오버헤드가 발생한다.
컬렉션 (ArrayList 등)
- 내부적으로 배열을 사용하지만, 용량이 꽉 차면 알아서 더 큰 메모리 공간을 할당받아 데이터를 복사하는 동적 확장(Dynamic Resizing) 처리를 백그라운드에서 수행한다.
- 개발자는 크기 걱정 없이 add() 메서드만 호출하면 된다.
② 내장 API(기능)의 차이
배열
- 데이터의 저장과 인덱스를 통한 읽기라는 최소한의 기능만 제공한다.
- 중간에 있는 데이터를 삭제하고 뒤의 데이터들을 앞으로 당기거나, 특정 데이터를 검색하고 정렬하려면 개발자가 직접 복잡한 반복문 로직을 구현해야 한다.
컬렉션
- 데이터를 다루는 데 필요한 수많은 자료구조 알고리즘(삽입, 삭제, 검색, 정렬, 크기 확인 등)이 메서드 형태로 이미 완벽하게 구현되어 제공된다.
3. 배열과 컬렉션의 타입 제한 차이
배열
- int, double 같은 기본형(Primitive) 타입과 객체(Reference) 타입을 모두 저장할 수 있다.
컬렉션
- 다형성을 기반으로 설계되었기 때문에 오직 참조형 객체(Object)만 저장할 수 있다.
- 따라서 기본형 데이터를 컬렉션에 담으려면 이전 포스트에서 다룬 Wrapper 클래스(Integer, Double 등)로 박싱(Boxing)하는 과정이 필수적이다.
💻 코드로 보는 배열과 컬렉션의 차이
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayVsCollection {
public static void main(String[] args) {
// [1. 배열의 한계]
// 선언 시 반드시 크기(3)를 지정해야 하며, 이후 늘릴 수 없음.
int[] arr = new int[3];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
// arr[3] = 40; // 💥 에러! 배열의 크기인 3칸을 넘어서 접근 불가
// [2. 컬렉션(ArrayList)의 유연성]
// 크기를 지정하지 않아도 되며, 기본형 int 대신 Wrapper 클래스 Integer 사용
ArrayList<Integer> list = new ArrayList<>();
// add() 메서드로 무한히 데이터를 밀어 넣을 수 있음 (동적 크기 할당)
list.add(10);
list.add(20);
list.add(30);
list.add(40); // ⭕ 공간이 알아서 늘어나므로 정상 작동!
// [3. 기능(API)의 차이]
// 중간 데이터(인덱스 1번, 20)를 삭제할 때의 차이
// 배열: 직접 뒤의 요소(30)를 앞으로 당기고 처리해야 함 (매우 번거로움)
arr[1] = arr[2];
arr[2] = 0;
// 컬렉션: remove() 메서드 하나면 내부에서 알아서 요소를 당겨줌
list.remove(1);
}
}