자바 백엔드 입문 5편. 자바 코드 매일 쓰는 컬렉션 — List·Set·Map과 ArrayList·LinkedList·HashMap·HashSet 차이를 책장 비유로 풀어쓴 학습 노트.
이 글은 자바 백엔드 입문 시리즈 59편 중 5편이에요. 4편 어노테이션 까지 자바의 "한 객체" 단위를 다뤘다면, 이번 5편은 "여러 객체를 묶어 다루는 도구" 인 자바 컬렉션 을 풀어 가요.
컬렉션이 헷갈리는 이유
자바 코드 어디에나 List<String>·Map<Long, User> 같은 표현이 등장해요. 처음 보면 "List? Set? Map? 뭐가 다른데?" 가 안 잡혀요.
이 글에서는 책장 비유로 풀어요. List = "순서대로 꽂힌 책장", Set = "중복 없는 책 모음", Map = "색인이 붙은 사물함". 끝까지 따라오시면 세 가지 + 자주 만나는 6가지 구현체가 한 그림에 들어와요.
3가지 핵심 인터페이스
자바 컬렉션은 큰 범주 세 가지로 나뉘어요.
| 인터페이스 | 의미 | 비유 |
|---|---|---|
| List | 순서 있음, 중복 허용 | 순서대로 꽂힌 책장 |
| Set | 순서 없음, 중복 불허 | 중복 없는 책 모음 |
| Map | 키-값 쌍 | 색인 붙은 사물함 |
90% 시나리오 = List 또는 Map. Set은 가끔.
List — 순서 있는 묶음
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Alice"); // 중복 OK
System.out.println(names); // [Alice, Bob, Alice]
System.out.println(names.get(0)); // Alice (인덱스 접근)
System.out.println(names.size()); // 3
자주 쓰는 메서드 — add·get·size·remove·contains·indexOf·isEmpty.
가장 흔한 구현체 = ArrayList. 내부가 배열이라 인덱스 접근 빠름(O(1)).
LinkedList 도 있는데 — 중간 삽입·삭제 빈번할 때 유리. 다만 실무에서 99% ArrayList. 메모리·캐시 효율 차원이 압도적.
Set — 중복 없는 묶음
Set<String> tags = new HashSet<>();
tags.add("자바");
tags.add("Spring");
tags.add("자바"); // 중복 무시
System.out.println(tags.size()); // 2
System.out.println(tags.contains("자바")); // true (O(1))
구현체 3가지:
- HashSet — 순서 보장 X, 가장 빠름 (O(1) 평균)
- LinkedHashSet — 삽입 순서 유지
- TreeSet — 정렬된 순서 (O(log n))
가장 흔한 = HashSet.
Map — 키-값 쌍
Map<Long, String> users = new HashMap<>();
users.put(1L, "Alice");
users.put(2L, "Bob");
users.put(1L, "Alice 2"); // 같은 키 덮어씀
System.out.println(users.get(1L)); // "Alice 2"
System.out.println(users.size()); // 2
System.out.println(users.containsKey(2L)); // true
자주 쓰는 메서드 — put·get·remove·containsKey·keySet·values·entrySet.
구현체 3가지 (Set과 평행):
- HashMap — 가장 흔함, O(1)
- LinkedHashMap — 삽입 순서 유지
- TreeMap — 키 정렬 (O(log n))
ArrayList vs LinkedList — 언제 무엇?
| ArrayList | LinkedList | |
|---|---|---|
| 내부 구조 | 동적 배열 | 양방향 연결 리스트 |
| 인덱스 접근 | O(1) 매우 빠름 | O(n) 느림 |
| 끝에 추가 | O(1) | O(1) |
| 중간 삽입·삭제 | O(n) — 뒷 요소 이동 | O(1) — 노드 연결만 |
| 메모리 | 효율 | 노드마다 포인터 부담 |
실무 99% = ArrayList. "중간 삽입 많은 시나리오" 가 실제로는 드물고, ArrayList도 충분히 빠름. LinkedList는 "이론적으로만 좋은" 경우 많음.
HashMap vs TreeMap — 언제 무엇?
| HashMap | TreeMap | |
|---|---|---|
| 순서 | 없음 | 키 정렬 |
| put/get | O(1) | O(log n) |
| 메모리 | 작음 | 큼 (트리 노드) |
| 사용 | 90% — 빠른 조회 | 정렬 필요 시 |
대부분 HashMap. "키를 정렬해 순회" 같은 요구가 명확할 때만 TreeMap.
컬렉션 생성·초기화 패턴
자바 9+ 팩토리 메서드 (불변) — 가장 깔끔.
List<String> names = List.of("Alice", "Bob", "Charlie");
Set<Long> ids = Set.of(1L, 2L, 3L);
Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25);
불변 컬렉션이라 add() 호출 시 예외. "읽기 전용 상수" 같은 데에 표준.
가변 컬렉션이 필요하면:
List<String> names = new ArrayList<>(List.of("Alice", "Bob"));
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 30);
컬렉션 순회 — for-each
List<String> names = List.of("Alice", "Bob");
// for-each (가장 흔함)
for (String name : names) {
System.out.println(name);
}
// Map 순회
Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25);
for (Map.Entry<String, Integer> entry : ages.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
자바 8+ Stream 으로도 가능 (9편 Stream API 에서 깊이).
Spring·JPA 코드에서 자주
이 시리즈의 거의 모든 글에 컬렉션이 등장해요.
// 컨트롤러 ([29편](https://smartlifen4n.com/request-parameters/))
@GetMapping
public List<OrderResponse> list(@RequestParam(required = false) List<String> statuses) { ... }
// JPA Repository ([45편](https://smartlifen4n.com/entity-repository/))
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByStatus(String status);
}
// 매개변수
public void process(Map<String, Object> params) { ... }
자바 백엔드 = 컬렉션 다루는 일.
순서 + 중복 OK → ArrayList. 중복 안 됨 → HashSet. 키로 빠른 조회 → HashMap. 정렬 필요 시 TreeMap. 90% 시나리오 이 룰로 끝.
한 줄 정리 — 자바 컬렉션 3대 = List(순서·중복)·Set(중복X)·Map(키-값). 실무 표준 = ArrayList·HashSet·HashMap. 불변은 List.of·Set.of·Map.of.
시험 직전 한 번 더 — 자바 컬렉션 입문자가 매번 헷갈리는 것
- 3대 인터페이스 = List·Set·Map
- List = 순서 있음, 중복 허용. 인덱스 접근
- Set = 순서 없음, 중복 불허. 빠른 contains
- Map = 키-값 쌍. 빠른 키 조회
- 가장 흔한 구현체 =
ArrayList·HashSet·HashMap - ArrayList vs LinkedList = 실무 99% ArrayList
- HashMap vs TreeMap = HashMap (90%), TreeMap (정렬 필요 시)
- LinkedHashSet/LinkedHashMap = 삽입 순서 유지
List.of(...)·Set.of(...)·Map.of(...)= 불변 컬렉션 (자바 9+)- 가변 =
new ArrayList<>(...)·new HashMap<>() add·get·size·remove·contains= List 표준put·get·containsKey·keySet·entrySet= Map 표준- for-each =
for (T t : collection) - Map 순회 =
entrySet()후getKey()·getValue() - 빈 컬렉션 =
Collections.emptyList()또는List.of() Arrays.asList(...)= 자바 8 옛 방식 (불완전 불변)- 컬렉션 안
null= HashMap·HashSet은 OK, TreeMap·TreeSet은 NullPointerException - 멀티스레드 안전 =
ConcurrentHashMap·CopyOnWriteArrayList - 일반 HashMap을 여러 스레드가 만지면 위험 — 데이터 손상 가능
- 자바 백엔드 = 매일 다루는 자료구조
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
다음 글: