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 — 다른 버킷record ProductKey(String sku, String region) {} // equals + hashCode 자동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가 삽입 후 바뀌면 그 원소는 찾을 수도 지울 수도 없다.
Set<List<Integer>> set = new HashSet<>();
set.add(List.of(1, 2)); // 불변 키만 사용. record, String, Integer, List.of 등List<String> a = List.of("a", null); // NullPointerException
List<String> b = Arrays.asList("a", "b");
b.add("c"); // UnsupportedOperationException (고정 크기)List<String> a = new ArrayList<>(); a.add(null); // null 필요하면 가변 리스트
List<String> b = new ArrayList<>(Arrays.asList("a", "b")); b.add("c"); // 복사해서 가변으로class Cart {
private final List<Item> items = new ArrayList<>();
public List<Item> getItems() { return items; } // 외부에서 add/clear 가능 → 캡슐화 파괴
}
cart.getItems().clear(); // 장바구니가 비워짐public List<Item> getItems() { return List.copyOf(items); } // 불변 복사본
// 또는 Collections.unmodifiableList(items) — 뷰(원본 변경은 반영됨, 외부 수정만 차단)for (String key : map.keySet()) {
if (map.get(key) == 0) map.remove(key); // CME
}map.entrySet().removeIf(e -> e.getValue() == 0);
// 또는 map.values().removeIf(v -> v == 0);Map<String, Long> report = new HashMap<>();
// ... 채운 뒤
report.forEach((k, v) -> System.out.println(k + ": " + v)); // 순서가 매번 다를 수 있음HashMap의 순회 순서는 해시와 테이블 크기에 의존하며, 확장되면 바뀐다. 테스트에서 우연히 맞다가 데이터가 늘면 순서가 바뀐다.
Map<String, Long> report = new TreeMap<>(); // 키 정렬
// 또는 new LinkedHashMap<>() // 삽입 순서
// 또는 출력 직전에 정렬: report.entrySet().stream().sorted(Map.Entry.comparingByKey())...