← Java 강의 목록으로
📦
컬렉션 · 제네릭
컬렉션 · 제네릭 · 선수: 이전 단원

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("영어"));  // false

4) 불변 팩토리

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);
    }
}

자주 하는 실수

  1. `HashSet` 순서가 일정하다고 가정
  2. `HashMap` 에 `null` 키를 넣은 다음 잊고 `NullPointerException`
  3. 컬렉션을 순회 중 직접 `remove` 호출 → `ConcurrentModificationException`
  4. `Arrays.asList(new int[]{1,2,3})` 가 `List<Integer>` 가 아니라 `List<int[]>` 인 점
  5. `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_제네릭/) — 제네릭 클래스/메서드와 와일드카드를 배웁니다.

예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗