프로그래밍에서 데이터를 효율적으로 저장하고 관리하기 위해 컬렉션 프레임워크는 필수적이다. 다양한 상황에 맞춰 최적의 성능을 내기 위해서는 각 컬렉션의 특징을 이해하고 적절한 구현 클래스를 선택하는 것이 중요하다.
순서가 있는 데이터의 집합으로, 데이터의 중복을 허용합니다. 인덱스를 통해 특정 위치의 데이터에 빠르게 접근할 수 있다.
| 구현 클래스 | 특징 | 추천 사용 상황 |
|---|---|---|
| ArrayList | - 내부적으로 배열(Array)을 사용하여 데이터 저장 - 인덱스를 통한 데이터 조회(get)가 매우 빠름 - 데이터의 추가/삭제 시, 특히 중간에 삽입/삭제 시 성능 저하 발생 가능 (데이터 이동 필요) | - 데이터 조회가 빈번하게 일어나는 경우 - 데이터의 크기가 예측 가능하고, 중간에 데이터 추가/삭제가 적은 경우 - 스택(Stack)처럼 리스트의 마지막에서만 추가/삭제가 일어나는 경우 |
| LinkedList | - 내부적으로 노드(Node)들이 서로 연결된 형태로 데이터 저장 - 데이터의 추가/삭제(add/remove)가 매우 빠름 (연결 정보만 변경) - 특정 인덱스의 데이터를 조회하려면 처음부터 순차적으로 탐색해야 하므로 조회 성능이 떨어짐 | - 데이터의 추가/삭제가 빈번하게 발생하는 경우 - 데이터의 순차적인 접근이 주를 이루는 경우 - 큐(Queue)나 스택(Stack) 자료구조를 구현할 때 (양 끝에서의 추가/삭제가 빠르므로) |
| Vector | - ArrayList와 동일한 구조 - 모든 메소드가 동기화(synchronized) 처리되어 있어 멀티 스레드 환경에서 안전(Thread-safe) - 동기화 처리로 인해 단일 스레드 환경에서는 ArrayList보다 성능이 떨어짐 | - 멀티 스레드 환경에서 순서가 있는 목록을 안전하게 관리해야 할 때 |
| Stack | - Vector를 상속받아 구현 - LIFO(Last-In, First-Out) 후입선출 구조 | - 재귀 알고리즘, 실행 취소(Undo) 기능, 웹 브라우저의 뒤로 가기/앞으로 가기 기능 구현 시 |
키(Key)와 값(Value)을 하나의 쌍으로 묶어 저장하는 방식. 키는 중복될 수 없으며, 값은 중복이 가능하다. 키를 통해 값을 빠르게 찾아낼 수 있다.
| 구현 클래스 | 특징 | 추천 사용 상황 |
|---|---|---|
| HashMap | - 가장 일반적으로 사용되는 Map 구현 클래스 - 해싱(Hashing)을 사용하여 키를 기반으로 값을 매우 빠르게 검색 (시간 복잡도: O(1)) - 입력 순서를 보장하지 않음 - null 키와 null 값을 허용 | - 순서에 상관없이, 키를 통해 데이터를 빠르게 저장하고 조회하는 것이 중요한 대부분의 경우 |
| LinkedHashMap | - HashMap의 특징을 모두 가짐 - 데이터가 입력된 순서를 기억하고, 그 순서대로 순회(iterate) 가능 | - 입력된 순서대로 데이터를 처리하거나 출력해야 할 때 - 캐시(Cache) 구현 시 LRU(Least Recently Used) 알고리즘을 적용하기 용이 |
| TreeMap | - 키(Key)를 기준으로 자동 정렬되는 Map - 레드-블랙 트리(Red-Black Tree)로 구현되어 있음 - 정렬로 인해 HashMap보다 데이터 추가/삭제 및 검색 속도가 느릴 수 있음 (시간 복잡도: O(logN)) | - 저장된 데이터를 키 순서대로 정렬해야 할 때 - 특정 범위의 데이터를 검색해야 할 때 (예: 특정 점수 이상의 학생 목록 조회) |
| Hashtable | - HashMap과 동일한 구조 - 모든 메소드가 동기화(synchronized) 처리되어 있어 멀티 스레드 환경에서 안전 - null 키와 null 값을 허용하지 않음- 최근에는 ConcurrentHashMap 사용이 더 권장됨 | - (레거시) 멀티 스레드 환경에서 키-값 데이터를 안전하게 관리해야 할 때 |
순서에 상관없이 데이터의 중복을 허용하지 않는 집합. 데이터의 존재 여부를 빠르게 확인하는 데 유용하다.
| 구현 클래스 | 특징 | 추천 사용 상황 |
|---|---|---|
| HashSet | - 가장 일반적으로 사용되는 Set 구현 클래스 - 해싱(Hashing)을 사용하여 데이터의 추가/삭제 및 검색 속도가 매우 빠름 (시간 복잡도: O(1)) - 저장된 순서를 보장하지 않음 | - 데이터의 중복을 제거하고, 순서에 상관없이 저장/조회할 때 - 특정 데이터가 컬렉션 안에 존재하는지 빠르게 확인해야 할 때 |
| LinkedHashSet | - HashSet의 특징을 모두 가짐 - 데이터가 입력된 순서를 기억하고, 그 순서대로 순회(iterate) 가능 | - 입력된 순서대로 중복 없는 데이터를 관리해야 할 때 |
| TreeSet | - 데이터를 기준으로 자동 정렬되는 Set - 레드-블랙 트리(Red-Black Tree)로 구현되어 있음 - 정렬로 인해 HashSet보다 데이터 추가/삭제 및 검색 속도가 느릴 수 있음 (시간 복잡도: O(logN)) | - 중복 없는 데이터를 항상 정렬된 상태로 유지해야 할 때 - 특정 범위의 데이터를 검색해야 할 때 (예: 특정 점수 이상의 학생 목록 조회) |
데이터를 일시적으로 저장하고, 먼저 들어온 데이터가 먼저 나가는 FIFO(First-In, First-Out) 선입선출 구조를 가짐. 데이터 처리 순서가 중요할 때 유용하게 사용된다.
| 구현 클래스 | 특징 | 추천 사용 상황 |
|---|---|---|
| LinkedList | - Queue 인터페이스를 구현하고 있어 큐 자료구조로 활용 가능- 데이터 추가/삭제가 빨라 큐의 동작 방식에 적합 | - 일반적인 FIFO 방식의 큐를 구현할 때 |
| PriorityQueue | - 저장된 데이터 중에서 우선순위가 가장 높은 데이터가 먼저 출력되는 큐 - 내부적으로 힙(Heap) 자료구조를 사용 - 데이터를 넣는 순서와 상관없이, 우선순위에 따라 정렬됨 | - 작업 스케줄링(우선순위가 높은 작업 먼저 처리) - 네트워크 트래픽 관리(긴급한 데이터 먼저 전송) - 다익스트라(Dijkstra)와 같은 그래프 최단 경로 탐색 알고리즘 |

가장 보편적이고 가독성이 좋은 표준 순회 방식이다. 내부적으로 Iterator를 사용하지만 훨씬 간결하게 표현할 수 있다.
List<Object> items = new ArrayList<>();
items.add("Hello, World!"); // String 타입
items.add(123); // Integer 타입
items.add(new User("Java")); // User 타입
for (Object item : items) {
// 현재 item 변수는 Object 타입이므로, toString(), equals() 등 Object의 메서드만 호출 가능
System.out.println("타입: " + item.getClass().getSimpleName() + ", 값: " + item.toString());
// 특정 타입의 메서드를 호출하려면 반드시 타입 확인 및 강제 형변환이 필요하다.
if (item instanceof String s) {
System.out.println(" -> 문자열 길이: " + s.length());
} else if (item instanceof Integer i) {
System.out.println(" -> 정수 값의 두 배: " + (i * 2));
} else if (item instanceof User u) {
System.out.println(" -> 사용자 이름: " + u.name());
}
Iterator는 자바 컬렉션 프레임워크의 표준 순회 방식이다. 모든 Collection 인터페이스의 구현체는 iterator() 메서드를 통해 Iterator 객체를 반환한다.
hasNext(): 다음에 가져올 요소가 있는지 확인한다 (boolean 반환).
next(): 다음 요소를 가져온다.
remove(): next()로 가져온 요소를 컬렉션에서 안전하게 제거한다. 반복문 실행 중 요소를 삭제할 수 있는 유일하고 안전한 방법이다.
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Cherry");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
// "Banana" 요소를 안전하게 제거
if ("Banana".equals(fruit)) {
iterator.remove();
}
}
System.out.println("삭제 후 Set: " + set); // [Apple, Cherry]
단순 순회를 넘어, 컬렉션의 데이터를 처리하는 데 중점을 둔 기능이다. 데이터를 필터링, 매핑, 정렬하는 등 다양한 중간 연산과 최종 연산을 조합하여 복잡한 작업을 간결하게 처리할 수 있다.
데이터 처리 로직을 선언형으로 작성하여 가독성이 높다.
내부 반복을 통해 병렬(parallel) 처리가 용이하다.
filter(), map(), sorted() 등 강력하고 유용한 중간 연산을 제공한다.
import java.util.ArrayList;
import java.util.List;
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("avocado");
list.add("cherry");
// 단순 순회
list.stream().forEach(System.out::println);
// 'a'로 시작하는 단어만 필터링하여 대문자로 변환 후 출력
list.stream()
.filter(s -> s.startsWith("a")) // 필터링 (중간 연산)
.map(String::toUpperCase) // 매핑 (중간 연산)
.forEach(System.out::println); // 최종 연산