📚 오늘 공부한 내용 - 컬렉션 유틸리티 & 전체 정리
🛠️ Collections 클래스 - 유용한 도구들
정렬 관련 메서드
List<Integer> list = new ArrayList<>();
list.add(1); list.add(2); list.add(3); list.add(4); list.add(5);
// 최댓값/최솟값 찾기
Integer max = Collections.max(list); // 5
Integer min = Collections.min(list); // 1
// 컬렉션 섞기 (랜덤)
Collections.shuffle(list); // [2, 4, 1, 5, 3] (매번 다름)
// 정렬
Collections.sort(list); // [1, 2, 3, 4, 5]
// 역순 정렬
Collections.reverse(list); // [5, 4, 3, 2, 1]
핵심 유틸리티 기능들
- max/min: 정렬 기준으로 최댓값/최솟값 반환
- shuffle: 컬렉션을 랜덤하게 섞음
- sort: 정렬 기준으로 컬렉션 정렬
- reverse: 현재 순서를 뒤집음 (정렬 기준의 반대가 아님!)
🔒 불변 컬렉션 생성
편리한 불변 컬렉션 (Java 9+)
// of() 메서드로 간단 생성
List<Integer> list = List.of(1, 2, 3);
Set<Integer> set = Set.of(1, 2, 3);
Map<Integer, String> map = Map.of(1, "one", 2, "two");
System.out.println("list class = " + list.getClass());
// java.util.ImmutableCollections$ListN
// 변경 시도하면 예외 발생
// list.add(4); // UnsupportedOperationException
빈 컬렉션 생성
// 빈 불변 컬렉션
List<Integer> empty1 = Collections.emptyList(); // Java 5+
List<Integer> empty2 = List.of(); // Java 9+ (권장)
// 빈 가변 컬렉션
List<Integer> mutable1 = new ArrayList<>();
List<Integer> mutable2 = new LinkedList<>();
권장사항: Java 9 이상에서는 List.of()가 더 간결하고 일관성 있음
🔄 가변 ↔ 불변 컬렉션 전환
// 불변 리스트 생성
List<Integer> immutableList = List.of(1, 2, 3);
// 불변 → 가변 전환
ArrayList<Integer> mutableList = new ArrayList<>(immutableList);
mutableList.add(4); // 변경 가능
System.out.println("mutableList = " + mutableList);
// 가변 → 불변 전환
List<Integer> backToImmutable = Collections.unmodifiableList(mutableList);
// backToImmutable.add(5); // UnsupportedOperationException
Arrays.asList()의 특이한 점
List<Integer> list = Arrays.asList(1, 2, 3);
// 특징: 크기는 고정, 요소는 변경 가능
list.set(0, 10); // ✅ 가능 - 기존 위치 요소 변경
// list.add(4); // ❌ 불가능 - 크기 변경
// list.remove(0); // ❌ 불가능 - 크기 변경
// 애매한 컬렉션이므로 List.of() 권장
List<Integer> better = List.of(1, 2, 3); // 완전 불변
🔐 멀티스레드 동기화
ArrayList<Integer> list = new ArrayList<>();
list.add(1); list.add(2); list.add(3);
// 동기화된 리스트로 변환
List<Integer> synchronizedList = Collections.synchronizedList(list);
System.out.println("synchronizedList class = " +
synchronizedList.getClass());
// java.util.Collections$SynchronizedRandomAccessList
특징:
- 멀티스레드 환경에서 안전하게 사용 가능
- 동기화 작업으로 인해 성능은 느려짐
- 멀티스레드 학습 후 상세히 이해 가능
🏛️ 컬렉션 프레임워크 전체 아키텍처
계층 구조 다이어그램
Iterable
↓
Collection ────────────── Map
↓ ↓
┌──────────────┼──────────────┐ ├── HashMap
List Set Queue ├── TreeMap
↓ ↓ ↓ └── LinkedHashMap
ArrayList HashSet ArrayDeque
LinkedList TreeSet LinkedList
LinkedHashSet
Collection 인터페이스의 핵심 역할
1. 일관성 (Consistency)
// 모든 컬렉션 타입이 동일한 메서드 제공
Collection<String> list = new ArrayList<>();
Collection<String> set = new HashSet<>();
Collection<String> queue = new ArrayDeque<>();
// 동일한 방식으로 사용 가능
list.add("item"); set.add("item"); queue.add("item");
list.size(); set.size(); queue.size();
list.isEmpty(); set.isEmpty(); queue.isEmpty();
2. 재사용성 (Reusability)
// 하나의 메서드로 모든 컬렉션 처리
public static void printSize(Collection<?> collection) {
System.out.println("Size: " + collection.size());
}
printSize(new ArrayList<>()); // List 처리
printSize(new HashSet<>()); // Set 처리
printSize(new ArrayDeque<>()); // Queue 처리
3. 다형성 (Polymorphism)
// 런타임에 실제 구현체가 결정됨
public static Collection<String> createCollection(String type) {
switch(type) {
case "list": return new ArrayList<>();
case "set": return new HashSet<>();
case "queue": return new ArrayDeque<>();
default: throw new IllegalArgumentException();
}
}
주요 메서드들
- add(E e): 요소 추가
- remove(Object o): 요소 제거
- size(): 요소 개수 반환
- isEmpty(): 비어있는지 확인
- contains(Object o): 요소 포함 여부 확인
- iterator(): 반복자 반환
- clear(): 모든 요소 제거
📊 실무 선택 가이드
상황별 최적 구현체
요구사항 추천 구현체 이유
| 일반적인 리스트 | ArrayList | 빠른 접근, 메모리 효율 |
| 앞쪽 삽입/삭제 빈번 | LinkedList | O(1) 앞쪽 연산 |
| 중복 제거 | HashSet | O(1) 검색/삽입 |
| 순서 유지 + 중복 제거 | LinkedHashSet | 입력 순서 보장 |
| 정렬된 집합 | TreeSet | 자동 정렬 유지 |
| 키-값 저장 | HashMap | O(1) 검색/삽입 |
| 순서 유지 + 키-값 | LinkedHashMap | 입력 순서 보장 |
| 정렬된 키-값 | TreeMap | 키 기준 정렬 |
| 스택/큐 구조 | ArrayDeque | 최고 성능 |
실무 기본 선택 (90% 상황)
// 가장 많이 사용되는 조합
List<String> list = new ArrayList<>(); // 순서 있는 데이터
Set<String> set = new HashSet<>(); // 중복 없는 데이터
Map<String, Object> map = new HashMap<>(); // 키-값 쌍 데이터
Deque<String> deque = new ArrayDeque<>(); // 스택/큐 구조
🎯 설계 원칙과 패턴
1. 인터페이스 기반 설계
// ✅ 권장: 인터페이스 타입 선언
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
// ❌ 비권장: 구체 클래스 타입 선언
ArrayList<String> list = new ArrayList<>();
HashMap<String, Integer> map = new HashMap<>();
2. 전략 패턴 적용
- 정렬: Comparable(기본 전략) + Comparator(대체 전략)
- 반복: Iterator 패턴으로 순회 전략 추상화
- 구현체: 동일 인터페이스의 다양한 구현 전략
3. 팩토리 패턴 활용
// Collections의 팩토리 메서드들
List<String> emptyList = Collections.emptyList();
List<String> singletonList = Collections.singletonList("item");
Map<String, Integer> emptyMap = Collections.emptyMap();
🔍 컬렉션 프레임워크의 3대 구성요소
1. 인터페이스 (Interface)
- 역할: 추상적인 데이터 타입 정의
- 예시: Collection, List, Set, Queue, Map
- 장점: 구현에 독립적인 프로그래밍
2. 구현 (Implementation)
- 역할: 인터페이스의 구체적인 구현체
- 예시: ArrayList, HashMap, TreeSet 등
- 특징: 각각 다른 성능 특성과 용도
3. 알고리즘 (Algorithm)
- 역할: 데이터 처리 및 조작 기능
- 위치: 각 구현체 + Collections/Arrays 클래스
- 예시: 정렬, 검색, 순환, 변환 등
💡 핵심 깨달음
"만드는 사람이 수고로우면 쓰는 사람이 편하다"
컬렉션 프레임워크는 이 철학을 완벽히 보여주는 사례입니다:
- Java 설계자들의 수고: 복잡한 자료구조와 알고리즘 구현
- 개발자의 편리함: 간단한 메서드 호출로 모든 기능 사용
추상화의 계층
사용자 코드
↓
인터페이스 (List, Set, Map...)
↓
구현체 (ArrayList, HashMap...)
↓
내부 알고리즘 (정렬, 해싱, 트리...)
각 계층이 명확히 분리되어 관심사의 분리가 잘 이루어짐
확장성과 유지보수성
- 새로운 구현체 추가: 기존 코드 변경 없음
- 성능 개선: 내부 구현만 변경, API 동일 유지
- 기능 확장: 새로운 인터페이스/메서드 추가
🎯 오늘의 핵심 포인트
- Collections 유틸리티: 정렬, 섞기, 최댓값/최솟값 등 편의 기능
- 불변 컬렉션: List.of() 등으로 안전하고 간편한 생성
- 전환의 유연성: 가변 ↔ 불변 컬렉션 간 자유로운 변환
- 일관된 설계: Collection 인터페이스 중심의 통합 아키텍처
- 실용적 선택: 90% 상황에서 ArrayList, HashMap, HashSet, ArrayDeque
'🧑💻Sparta' 카테고리의 다른 글
| TIL - DDD 첫 설계 (10.28) (0) | 2025.10.28 |
|---|---|
| TIL - PUT 과 PATCH 멱등성 (10.14) (0) | 2025.10.14 |
| TIL - Comparator (09.25) (0) | 2025.09.25 |
| TIL - Iterator (09.24) (0) | 2025.09.24 |
| TIL - Deque (09.23) (0) | 2025.09.23 |