코딩/알고리즘 공부하면서
Map vs Set
americanoallday
2025. 6. 1. 16:57
✅ 1. Map vs Set 언제 쓰는가?
상황 | Map 사용 | Set 사용 |
키-값 쌍이 필요할 때 | ✅ | ❌ |
특정 “값”이 이미 존재하는지 검사할 때 | 가능하지만 비효율적 | ✅ 빠름 |
중복 없이 목록 저장할 때 | ❌ | ✅ |
각 키에 여러 값을 연결할 때 (ex. 그래프) | ✅ (Map<key, List>) | ❌ |
✳️ 요약
- 📦 Map: 어떤 Key로 Value를 찾고 싶을 때
- 🧺 Set: 중복 없이 값을 저장하고, “이 값 있나?” 빠르게 검사할 때
✅ 2. Map의 주요 구현체들과 특성
구현체 | 정렬 | 특징 | 시간 복잡도 | 사용 예 |
HashMap | ❌ 없음 | 가장 빠름 | O(1) 평균 | 일반적인 키-값 저장 |
LinkedHashMap | ✅ 삽입 순서 유지 | 순서 보존 + LRU 구현 가능 | O(1) | 캐시, 순서가 중요한 설정 |
TreeMap | ✅ 키 기준 정렬 | 항상 정렬 상태 유지 | O(log n) | 정렬된 탐색, 범위 검색 |
✅ 구조 비교
- HashMap: 내부적으로 해시 테이블 사용 (빠름, 순서 없음)
- LinkedHashMap: 해시 + 이중 연결 리스트 (순서 유지)
- TreeMap: 레드블랙 트리 (정렬 + 균형 트리)
✅ 3. Set의 주요 구현체들과 특성
구현체 | 정렬 | 특징 | 시간 복잡도 | 사용 예 |
HashSet | ❌ 없음 | 가장 빠름 | O(1) 평균 | 방문 기록, 중복 제거 |
LinkedHashSet | ✅ 삽입 순서 유지 | 순서 보존 | O(1) | 순서가 중요한 중복 제거 |
TreeSet | ✅ 자동 정렬 | 정렬된 집합 유지 | O(log n) | 정렬된 출력, 범위 검색 |
✅ 구조 비교
- HashSet: 내부적으로 HashMap<E, Object>처럼 동작
- LinkedHashSet: 해시 + 이중 연결 리스트
- TreeSet: 내부적으로 TreeMap 기반의 Red-Black Tree 사용
🧠 요약표
인터페이스 | 구현체 | 특징 | 적합한 용도 |
Map | HashMap | 빠른 key-value 저장 | 일반 딕셔너리 |
LinkedHashMap | 순서 유지 | LRU 캐시 등 | |
TreeMap | 정렬된 key | 범위 탐색, 정렬된 키 처리 | |
Set | HashSet | 빠르고 단순 | 방문 기록, 중복 제거 |
LinkedHashSet | 순서 보존 | 중복 제거 + 순서 유지 | |
TreeSet | 자동 정렬 | 정렬된 데이터 보관 |
📌 언제 뭘 써야 할까? (실전 기준)
상황 | 추천 |
중복 없는 값 저장 + 빠른 검사 | HashSet |
키로 값 저장하고 빠르게 검색 | HashMap |
입력 순서 유지 + 캐시 구현 | LinkedHashMap |
항상 정렬된 상태 유지 | TreeSet / TreeMap |
🔍 실전 예제
✅ 중복 없는 도시 저장 (Set)
Set<String> cities = new HashSet<>();
cities.add("Seoul");
cities.add("Seoul"); // 무시됨
✅ 사용자 ID → 나이 매핑 (Map)
Map<String, Integer> userAges = new HashMap<>();
userAges.put("user1", 25);
System.out.println(userAges.get("user1")); // 25
🎯 결론
- Map은 key → value를 빠르게 매핑할 때 쓰고
- Set은 중복 없는 값 모음이 필요할 때 쓴다
- 구현체(HashMap, TreeMap, LinkedHashSet 등)는
- 상황에 따라 성능 or 순서 or 정렬을 기준으로 고르면 된다
✅ 1. Map vs Set 차이
항목 | Map | Set |
목적 | Key → Value 쌍 저장 | 중복 없는 값 모음 저장 |
데이터 구조 | {K: V} 형태 | {V} 형태 |
사용 예시 | {"user1": 25} → 사용자 나이 매핑 | {1, 2, 3} 중복 없이 정수 저장 |
인터페이스 | Map<K, V> | Set<E> |
예제
Map<String, Integer> ageMap = new HashMap<>();
ageMap.put("Alice", 25); // key = "Alice", value = 25
Set<String> cities = new HashSet<>();
cities.add("Seoul");
cities.add("Seoul"); // 중복 → 무시됨
✅ 2. Map vs HashMap 차이
항목 | Map | HashMap |
개념 | 인터페이스 | 구현 클래스 |
인스턴스 생성 가능 여부 | ❌ 인터페이스라 안 됨 | ⭕ 직접 생성 가능 |
목적 | 다양한 Map 구조 지원 (TreeMap, LinkedHashMap 등) | 가장 일반적이고 빠른 Map 구현체 |
Map<String, Integer> map = new HashMap<>();
→ Map이라는 인터페이스로 선언하고 HashMap이라는 구현체로 생성함 → 유지보수 유리!
✅ 3. Map<Integer, List<Integer>>의 구조
정수 → 정수 리스트 매핑
즉, 한 정수 키에 여러 개의 정수가 연결되는 구조.
예시:
Map<Integer, List<Integer>> graph = new HashMap<>();
graph.put(1, Arrays.asList(2, 3));
→ “1번 노드는 2번, 3번 노드로 연결돼 있음” 같은 그래프 표현 구조
📌 이 구조는 그래프의 인접 리스트를 표현할 때 매우 자주 사용돼.
✅ 4. graph.computeIfAbsent(from, x -> new ArrayList<>()).add(to);
👇 이 한 줄의 의미는:
- from이라는 key가 없다면 → new ArrayList<>()로 초기화하고
- 그 리스트에 to를 추가해라
직관적 예:
if (!graph.containsKey(from)) {
graph.put(from, new ArrayList<>());
}
graph.get(from).add(to);
➡ 위 코드와 같은 동작을 한 줄로 압축한 버전
왜 쓰냐면?
- 깔끔하고 안전한 초기화 + 추가 방식이기 때문
✅ 5. 이 선언들이 의미하는 것
Set<Integer> component = new HashSet<>();
Queue<Integer> queue = new LinkedList<>();
📌 component
- 현재 컴포넌트(그래프 덩어리)에 포함된 정점들의 집합
- BFS로 연결된 모든 정점들을 여기다가 담음
- Set을 쓴 이유 → 중복 방지 + 빠른 포함 여부 확인 (O(1))
📌 queue
- BFS에서 사용할 탐색 대기열
- 먼저 넣은 정점을 먼저 꺼내야 하니까 Queue 구조 사용
- LinkedList는 Queue 인터페이스를 구현하므로 사용 가능
🎯 요약표
선언 | 목적 | 이유 |
Map<K, V> | Key → Value 매핑 | 그래프, 설정 정보 등에 활용 |
Set<E> | 중복 없는 값 모음 | 방문 정점 관리, 중복 제거 |
HashMap, HashSet | 빠른 구현체 | 평균 탐색 속도 O(1) |
Map<Integer, List<Integer>> | 그래프 표현 (인접 리스트) | 하나의 정점이 여러 정점과 연결된 구조 |
computeIfAbsent | 초기화 + 추가 합친 표현 | 코드 간결성 |
component | 현재 그래프 덩어리의 정점 집합 | 중복 방지 |
queue | BFS용 큐 | 탐색 순서 제어 |