공공부하자개발 · 영어 학습 노트
자바
중급객체지향과 코어 라이브러리0/9 완료
  • 01상속, 다형성, 오버라이딩
  • 02추상 클래스 vs 인터페이스
  • 03예외 처리
  • 04java.lang 심화
  • 05컬렉션 프레임워크 딥다이브
  • 06메서드 활용 패턴 (중급)
  • 07Object 메서드와 비교
  • 08java.time 실무 날짜 계산
  • 09HTTP 와 JSON 기초
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 중급 › 05 / 9

컬렉션 프레임워크 딥다이브

섹션 7진행 0 / 9
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

5. 자주 하는 실수 (Tip)

❌ 실수 1: hashCode 없이 HashMap 키로 사용

java
class ProductKey {
    String sku; String region;
    @Override public boolean equals(Object o) { /* sku, region 비교 */ }
    // hashCode 누락
}
Map<ProductKey, Integer> stock = new HashMap<>();
stock.put(new ProductKey("A", "KR"), 10);
stock.get(new ProductKey("A", "KR"));   // null — 다른 버킷
java
record ProductKey(String sku, String region) {}   // equals + hashCode 자동

❌ 실수 2: 변경 가능한 객체를 Set/Map 키로 쓰고 나서 수정

java
List<Integer> key = new ArrayList<>(List.of(1, 2));
Set<List<Integer>> set = new HashSet<>();
set.add(key);
key.add(3);                     // hashCode가 바뀜 → 원래 버킷에 없음
set.contains(key);              // false
set.remove(key);                // 제거도 안 됨 → 유령 원소

키의 hashCode가 삽입 후 바뀌면 그 원소는 찾을 수도 지울 수도 없다.

java
Set<List<Integer>> set = new HashSet<>();
set.add(List.of(1, 2));         // 불변 키만 사용. record, String, Integer, List.of 등

❌ 실수 3: List.of에 null 넣기, Arrays.asList에 add 하기

java
List<String> a = List.of("a", null);          // NullPointerException
List<String> b = Arrays.asList("a", "b");
b.add("c");                                   // UnsupportedOperationException (고정 크기)
java
List<String> a = new ArrayList<>(); a.add(null);            // null 필요하면 가변 리스트
List<String> b = new ArrayList<>(Arrays.asList("a", "b")); b.add("c");   // 복사해서 가변으로

❌ 실수 4: 내부 컬렉션을 그대로 반환

java
class Cart {
    private final List<Item> items = new ArrayList<>();
    public List<Item> getItems() { return items; }   // 외부에서 add/clear 가능 → 캡슐화 파괴
}
cart.getItems().clear();   // 장바구니가 비워짐
java
public List<Item> getItems() { return List.copyOf(items); }   // 불변 복사본
// 또는 Collections.unmodifiableList(items)  — 뷰(원본 변경은 반영됨, 외부 수정만 차단)

❌ 실수 5: 순회 중 Map 수정

java
for (String key : map.keySet()) {
    if (map.get(key) == 0) map.remove(key);   // CME
}
java
map.entrySet().removeIf(e -> e.getValue() == 0);
// 또는 map.values().removeIf(v -> v == 0);

❌ 실수 6: 정렬된 결과가 필요한데 HashMap 순회

java
Map<String, Long> report = new HashMap<>();
// ... 채운 뒤
report.forEach((k, v) -> System.out.println(k + ": " + v));   // 순서가 매번 다를 수 있음

HashMap의 순회 순서는 해시와 테이블 크기에 의존하며, 확장되면 바뀐다. 테스트에서 우연히 맞다가 데이터가 늘면 순서가 바뀐다.

java
Map<String, Long> report = new TreeMap<>();          // 키 정렬
// 또는 new LinkedHashMap<>()                        // 삽입 순서
// 또는 출력 직전에 정렬: report.entrySet().stream().sorted(Map.Entry.comparingByKey())...
자주 하는 실수 (Tip)
  • ❌ 실수 1: hashCode 없이 HashMap 키로 사용
  • ❌ 실수 2: 변경 가능한 객체를 Set/Map 키로 쓰고 나서 수정
  • ❌ 실수 3: List.of에 null 넣기, Arrays.asList에 add 하기
  • ❌ 실수 4: 내부 컬렉션을 그대로 반환
  • ❌ 실수 5: 순회 중 Map 수정
  • ❌ 실수 6: 정렬된 결과가 필요한데 HashMap 순회
이전 섹션4 응용 변형 예제5 / 7다음 섹션6 연습 문제