Quick Comparison
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
Map<String, Integer> map = new HashMap<>();| 데이터 성질 | 먼저 고를 타입 | 바로 확인할 경계 |
|---|---|---|
| 순서와 중복 유지 | List | random access가 많은지, 중간 삽입이 많은지 |
| 값의 유일성 | Set | equality/hash 또는 sorted comparator |
| key로 값 찾기 | Map | null·duplicate key·순서 정책 |
| 삽입 순서가 결과에 필요 | LinkedHashMap | access order가 필요한지 |
| key 순서 탐색 | TreeMap | comparator와 O(log n) 비용 |
갈리는 기준
어떤 컬렉션을 먼저 고르면 되나
| 상황 | 먼저 떠올릴 것 |
|---|---|
| 순서가 중요하고 중복 허용 | List |
| 중복 제거 | Set |
| 키로 빠르게 조회 | Map |
| 삽입 순서 유지 Map | LinkedHashMap |
| 키 정렬 Map | TreeMap |
List, Set, Map: 정책의 차이
Java 컬렉션은 "데이터를 여러 개 담는 상자"가 아니라, 순서, 중복 허용 여부, 키-값 관계 같은 정책 차이를 타입으로 드러냅니다. 어떤 컬렉션을 고를지는 데이터의 성격에 따라 달라집니다.
// 순서가 중요하고 중복 허용
List<String> orders = new ArrayList<>();
// 중복 제거가 중요
Set<String> uniqueTags = new HashSet<>();
// 키로 빠르게 조회
Map<Long, User> userById = new HashMap<>();구현체 선택과 성능 특성
같은 List라도 ArrayList는 index 접근에 적합하고, 중간 삽입은 뒤 원소 이동 비용이 듭니다. LinkedList는 삽입 위치의 node를 이미 알고 있을 때 연결 변경은 작지만, index로 그 위치를 찾는 비용은 O(n)입니다. HashMap은 평균적인 조회·삽입에 적합하되 hash 분포와 equals/hashCode 계약에 의존하고, LinkedHashMap은 삽입 순서를 유지하며, TreeMap은 키 정렬 순서를 유지합니다.
// 순서 유지가 필요한 Map
Map<String, Integer> scores = new LinkedHashMap<>();
// 키 정렬이 필요한 Map
Map<String, Integer> sorted = new TreeMap<>();인터페이스 타입으로 받기
변수 타입을 구현체(ArrayList)보다 인터페이스(List)로 선언하면, 나중에 구현체를 바꿀 때 호출 코드를 수정하지 않아도 됩니다. 구현체 타입이 중요한 경우(예: LinkedList의 Deque 기능)에만 구체 타입을 씁니다.
List vs Set vs Map을 잘못 고르면 생기는 문제
컬렉션 선택은 단순 취향 문제가 아니라, 중복 처리 방식과 조회 비용을 같이 결정합니다. 처음부터 데이터 성격에 맞게 잡아야 뒤에서 중복 제거 로직이나 선형 탐색을 덜 붙이게 됩니다.
// ❌ 회원 ID를 List에 쌓고 contains로 중복 검사
List<Long> userIds = new ArrayList<>();
if (!userIds.contains(id)) {
userIds.add(id);
}
// ✅ 중복이 핵심이면 Set
Set<Long> userIds = new HashSet<>();
userIds.add(id);
// ❌ 사용자 조회를 List 순회로 해결
User found = users.stream()
.filter(u -> u.getId().equals(id))
.findFirst()
.orElse(null);
// ✅ ID 조회가 핵심이면 Map
Map<Long, User> userById = new HashMap<>();
User found = userById.get(id);HashSet과 HashMap의 key는 삽입 뒤 equals/hashCode에 쓰는 field를 바꾸면 찾을 수 없게 될 수 있습니다. mutable key 대신 불변 ID나 불변 값 객체를 쓰는 편이 안전합니다.
선택 기준
| 상황 | 적합한 선택 |
|---|---|
| 순서가 중요하고 중복 허용 | List (ArrayList 기본) |
| 중복 제거가 중요 | Set (HashSet 기본) |
| 키로 빠르게 조회 | Map (HashMap 기본) |
| 삽입 순서 유지 Map | LinkedHashMap |
| 키 정렬 순서 Map | TreeMap |
주의할 점
"여러 개 담아야 하니 일단 List"로 시작하면 중복 처리나 조회 성능에서 나중에 설계를 다시 짜게 됩니다.
// ❌ List로 중복 검사 — O(n) 탐색 반복
List<String> tags = new ArrayList<>();
if (!tags.contains(newTag)) { // O(n)
tags.add(newTag);
}
// ✅ Set으로 중복 자동 제거 — 평균 O(1)
Set<String> tags = new HashSet<>();
tags.add(newTag); // 자동으로 중복 무시
// ❌ List에서 ID로 User 찾기 — O(n)
User found = users.stream().filter(u -> u.getId() == id).findFirst().orElse(null);
// ✅ Map으로 평균 O(1) 조회
Map<Long, User> userById = new HashMap<>();
User found = userById.get(id);참고 링크
2 sources