📦
컬렉션 · 제네릭
컬렉션 · 제네릭 · 선수: 이전 단원
12. List · Set · Map
배열은 크기가 고정이지만 실무에서는 **추가/삭제가 자유로운 컬렉션** 을 더 자주 사용합니다. 가장 많이 쓰이는 세 가지는 `List`, `Set`, `Map` 입니다.
JavaCollectionsGenericsList · Set · Map
소요 시간
⏱ 약 1~1.5시간
난이도
📊 중급
선수 조건
🎯 이전 단원 또는 동등 지식
결과물
배열은 크기가 고정이지만 실무에서는 **추가/삭제가 자유로운 컬렉션** 을 더 자주 사용합니다. 가장 많이 쓰이는 세 가지는 `List`, `Set`, `Map` 입니다.
이 강의에서 배우는 것
- 1`ArrayList` 와 `LinkedList` 의 차이를 안다
- 2`HashSet`(순서 없음) 과 `TreeSet`(정렬) 의 동작 차이를 안다
- 3`HashMap` 으로 key → value 매핑을 만든다
- 4컬렉션을 `for-each` 로 순회한다
- 5`List.of(...)`, `Set.of(...)`, `Map.of(...)` 같은 **불변 컬렉션 팩토리** 를 안다
소개
배열은 크기가 고정이지만 실무에서는 **추가/삭제가 자유로운 컬렉션** 을 더 자주 사용합니다. 가장 많이 쓰이는 세 가지는 `List`, `Set`, `Map` 입니다.
핵심 개념
1) `List<E>`
java
import java.util.ArrayList;
import java.util.List;
List<String> langs = new ArrayList<>();
langs.add("Java");
langs.add("Spring");
langs.remove(0);
System.out.println(langs.size()); // 1
System.out.println(langs.get(0)); // Spring- `ArrayList` : 인덱스 접근이 빠르고, 끝에 추가가 빠름. 일반적인 기본 선택
- `LinkedList` : 양쪽 끝에서 추가/삭제가 빠름. 큐 용도로 가끔 사용
2) `Set<E>`
java
import java.util.HashSet;
import java.util.Set;
Set<String> tags = new HashSet<>();
tags.add("java");
tags.add("java"); // 중복 무시
System.out.println(tags.size()); // 1- `HashSet` : 빠른 추가/검색, **순서 없음**
- `TreeSet` : 자동 정렬, 약간 느림
3) `Map<K, V>`
java
import java.util.HashMap;
import java.util.Map;
Map<String, Integer> score = new HashMap<>();
score.put("국어", 90);
score.put("수학", 85);
System.out.println(score.get("국어")); // 90
System.out.println(score.containsKey("영어")); // false4) 불변 팩토리
java
List<String> l = List.of("A", "B", "C");
Set<Integer> s = Set.of(1, 2, 3);
Map<String, Integer> m = Map.of("a", 1, "b", 2);
// l.add("D"); // UnsupportedOperationException수정 불가, 가벼움, 자주 쓰는 패턴입니다.
핵심 예제
예제 1 — `ArrayListBasics.java` : `ArrayList` 사용
java
import java.util.ArrayList;
import java.util.List;
public class ArrayListBasics {
public static void main(String[] args) {
List<String> langs = new ArrayList<>();
langs.add("Java");
langs.add("Spring");
langs.add("Kotlin");
langs.remove("Kotlin");
for (String s : langs) System.out.println(s);
System.out.println("size=" + langs.size());
}
}**실행 결과**
text
Java
Spring
size=2**메모:** `remove(Object)` 와 `remove(int index)` 가 시그니처로 구분됩니다.
예제 2 — `SetBasics.java` : `HashSet` vs `TreeSet`
java
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetBasics {
public static void main(String[] args) {
Set<Integer> h = new HashSet<>();
Set<Integer> t = new TreeSet<>();
for (int v : new int[]{3, 1, 4, 1, 5, 9, 2, 6}) {
h.add(v);
t.add(v);
}
System.out.println("HashSet : " + h);
System.out.println("TreeSet : " + t); // 정렬
}
}**실행 결과 (HashSet 순서는 다를 수 있음)**
text
HashSet : [1, 2, 3, 4, 5, 6, 9]
TreeSet : [1, 2, 3, 4, 5, 6, 9]**메모:** `HashSet` 의 출력 순서는 보장되지 않습니다. **순서가 중요하면 `LinkedHashSet`/`TreeSet`** 을 쓰세요.
예제 3 — `MapBasics.java` : `HashMap`
java
import java.util.HashMap;
import java.util.Map;
public class MapBasics {
public static void main(String[] args) {
Map<String, Integer> score = new HashMap<>();
score.put("국어", 90);
score.put("수학", 85);
score.put("영어", 78);
for (Map.Entry<String, Integer> e : score.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
System.out.println("국어=" + score.get("국어"));
System.out.println("프랑스어=" + score.getOrDefault("프랑스어", -1));
}
}**실행 결과 (순서는 다를 수 있음)**
text
국어 -> 90
수학 -> 85
영어 -> 78
국어=90
프랑스어=-1**메모:** `getOrDefault` 는 키가 없을 때의 기본값을 한 줄로 처리합니다.
예제 4 — `ImmutableCollections.java` : 불변 팩토리
java
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ImmutableCollections {
public static void main(String[] args) {
List<String> l = List.of("A", "B", "C");
Set<Integer> s = Set.of(1, 2, 3);
Map<String, Integer> m = Map.of("a", 1, "b", 2);
System.out.println(l);
System.out.println(s);
System.out.println(m);
try {
l.add("D");
} catch (UnsupportedOperationException e) {
System.out.println("불변: 수정 불가");
}
}
}**실행 결과 (Set/Map 순서는 다를 수 있음)**
text
[A, B, C]
[3, 2, 1]
{b=2, a=1}
불변: 수정 불가**메모:** 코드 안에서 자주 쓰는 작은 상수 컬렉션엔 이 팩토리들이 깔끔합니다.
전체 예제 코드 (src/)
src/ArrayListBasics.java
java
import java.util.ArrayList;
import java.util.List;
public class ArrayListBasics {
public static void main(String[] args) {
List<String> langs = new ArrayList<>();
langs.add("Java");
langs.add("Spring");
langs.add("Kotlin");
langs.remove("Kotlin");
for (String s : langs) System.out.println(s);
System.out.println("size=" + langs.size());
}
}
src/ImmutableCollections.java
java
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ImmutableCollections {
public static void main(String[] args) {
List<String> l = List.of("A", "B", "C");
Set<Integer> s = Set.of(1, 2, 3);
Map<String, Integer> m = Map.of("a", 1, "b", 2);
System.out.println(l);
System.out.println(s);
System.out.println(m);
try {
l.add("D");
} catch (UnsupportedOperationException e) {
System.out.println("불변: 수정 불가");
}
}
}
src/MapBasics.java
java
import java.util.HashMap;
import java.util.Map;
public class MapBasics {
public static void main(String[] args) {
Map<String, Integer> score = new HashMap<>();
score.put("국어", 90);
score.put("수학", 85);
score.put("영어", 78);
for (Map.Entry<String, Integer> e : score.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
System.out.println("국어=" + score.get("국어"));
System.out.println("프랑스어=" + score.getOrDefault("프랑스어", -1));
}
}
src/SetBasics.java
java
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetBasics {
public static void main(String[] args) {
Set<Integer> h = new HashSet<>();
Set<Integer> t = new TreeSet<>();
for (int v : new int[]{3, 1, 4, 1, 5, 9, 2, 6}) {
h.add(v);
t.add(v);
}
System.out.println("HashSet : " + h);
System.out.println("TreeSet : " + t);
}
}
자주 하는 실수
- `HashSet` 순서가 일정하다고 가정
- `HashMap` 에 `null` 키를 넣은 다음 잊고 `NullPointerException`
- 컬렉션을 순회 중 직접 `remove` 호출 → `ConcurrentModificationException`
- `Arrays.asList(new int[]{1,2,3})` 가 `List<Integer>` 가 아니라 `List<int[]>` 인 점
- `List.of(...)` 결과를 수정하려고 시도
정리
- 가장 흔한 선택: `ArrayList`, `HashSet`, `HashMap`
- 정렬이 필요하면 `TreeSet`/`TreeMap`
- 작은 상수 컬렉션엔 `List.of` / `Map.of`
- 컬렉션 순회 중 수정은 반드시 `iterator.remove` 등 안전한 방식으로
과제
# 과제 - 12. List · Set · Map
## 문제 1 — 중복 제거 + 정렬
- 파일명: `Homework01.java`
- 핵심 개념: `Set`, `TreeSet`
요구사항
- `int[] arr = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}` 에서 중복을 제거하고 오름차순으로 출력합니다.
예상 출력
text
[1, 2, 3, 4, 5, 6, 9]## 문제 2 — 단어 빈도
- 파일명: `Homework02.java`
- 핵심 개념: `HashMap`, `getOrDefault`
요구사항
- 문자열 배열 `{"apple", "banana", "apple", "cherry", "banana", "apple"}` 의 단어 빈도를 출력합니다.
- 키 순서는 신경 쓰지 않아도 됩니다.
예상 출력 (순서 다를 수 있음)
text
apple=3
banana=2
cherry=1## 정답 확인 직접 풀어 본 후 [`answer/`](./answer/) 폴더의 정답과 비교해 보세요.
정답 코드 (homework/answer/)
answer/Homework01.java
java
import java.util.Set;
import java.util.TreeSet;
/** 중복 제거 + 정렬. */
public class Homework01 {
public static void main(String[] args) {
int[] arr = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
Set<Integer> s = new TreeSet<>();
for (int n : arr) s.add(n);
System.out.println(s);
}
}
answer/Homework02.java
java
import java.util.HashMap;
import java.util.Map;
/** 단어 빈도 계산. */
public class Homework02 {
public static void main(String[] args) {
String[] words = {"apple", "banana", "apple", "cherry", "banana", "apple"};
Map<String, Integer> count = new HashMap<>();
for (String w : words) {
count.put(w, count.getOrDefault(w, 0) + 1);
}
for (Map.Entry<String, Integer> e : count.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
}
}
직접 해 보기
bash
cd 03_컬렉션_제네릭/12_List_Set_Map/src
javac MapBasics.java
java MapBasics다음 단원
[13_제네릭](../13_제네릭/) — 제네릭 클래스/메서드와 와일드카드를 배웁니다.