좋아! 요청한 4가지 주제를 순서대로 개념 + 이유 + 예제 코드까지 아주 쉽게 풀어서 설명해줄게.
핵심 구조:
배열 + 연결리스트 (→ 트리로 진화됨)
Map<String, Integer> map = new HashMap<>();
Entry[] table 이라는 배열이 존재함key.hashCode()를 이용해서 배열의 인덱스(버킷 번호)를 구함| 버전 | 충돌 시 구조 | 성능 |
|---|---|---|
| Java 7 | Linked List | O(n) |
| Java 8~ | Linked List → Tree | O(log n) |
| 자료구조 | 정렬 여부 | 순서 유지 | 용도/특징 |
|---|---|---|---|
| HashSet | ❌ 무작위 | ❌ | 가장 기본적인 Set |
| LinkedHashSet | ❌ 삽입 순서 유지 | ✅ | 순서 중요할 때 사용 |
| TreeSet | ✅ 정렬됨 | ❌ | 정렬된 Set 필요 시 사용 (Comparable 또는 Comparator 필요) |
| EnumMap | ❌ 빠른 속도 | ✅ (enum 순서) | key가 enum인 경우에만 사용 |
| IdentityHashMap | ❌ 주소 비교 | ❌ | == 비교 사용, 동등성 무시 (성능 극단적 우선시할 때) |
Set<String> set = new LinkedHashSet<>();
set.add("B");
set.add("A");
System.out.println(set); // [B, A]
Set<String> sorted = new TreeSet<>();
sorted.add("B");
sorted.add("A");
System.out.println(sorted); // [A, B]
모든 자바 클래스는 Object를 상속받고, 기본적으로 hashCode()는 이렇게 동작함:
public native int hashCode(); // JVM에서 주소 기반으로 구현됨
class MyObj {}
MyObj a = new MyObj();
MyObj b = new MyObj();
System.out.println(a.hashCode()); // 예: 123456
System.out.println(b.hashCode()); // 예: 654321
→ 기본은 객체의 메모리 주소 기반으로 hashCode 생성됨
→ equals()가 다르면 hashCode()도 다름이 보장됨
✅ 하지만, hashCode()만 override하고 equals() 안 하면 문제가 생김 (HashSet 오작동)
hashCode() 값이 변하지 않음 → HashMap에서 안전하게 key로 사용 가능public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
for (int i = 0; i < value.length; i++) {
h = 31 * h + value[i];
}
hash = h;
}
return h;
}
hashCode()가 한 번 계산되면 캐시에 저장됨→ 해시 기반 자료구조(HashMap, HashSet)에서 가장 안정적인 key!
좋아, 너가 궁금해한 4가지를 아래처럼 정리하고, 각 항목을 기초 → 핵심 개념 → 코드 예제 → 실전 함정 포인트까지 단계적으로 다뤄줄 수 있어. 우선 전체 목차를 보여줄게. 이 중에서 원하는 주제부터 시작하자.
➤ 배열 + LinkedList + Tree 구조까지 실제 내부 소스 구조 기반으로 분석
배우게 될 것:
threshold = 8)load factor, initial capacity 의미+ 실전 해부 예제:
➤
String.hashCode()의 수학적 원리와 성능 최적화 비밀 분석
배우게 될 것:
31 * h + value[i] 구조의 수학적 의미+ 실전 예제:
➤ 실무에서 가장 많이 실수하는 부분 + 실기 문제 핵심 포인트
배우게 될 것:
equals()와 hashCode()의 연관성과 법칙Objects.hash(...), Objects.equals(...)의 사용법+ 실전 연습:
Person 클래스 비교 문제 (동명이인, 주민번호 비교 등)➤ 문제 스타일: "결과값을 예측하라", "동작하지 않는 이유는?"
문제 유형:
HashMap.containsKey 오작동 이유mutable key로 인한 검색 실패HashSet 중복 판별 실패 이유+ 연습 예제 포함:
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
HashSet<Person> set = new HashSet<>();
set.add(new Person("John", 20));
set.add(new Person("John", 20));
System.out.println(set.size());
보기:
정답: B. 2 → 이유: equals/hashCode를 오버라이드 안 했기 때문에 서로 다른 객체로 판단됨
좋아! 지금부터 정보처리기사 실기 시험 스타일로, 해시(HashMap, HashSet)의 원리와 트랩을 파고드는 실전 문제를 하나씩 내고, 디버깅하듯 상세히 풀이해줄게.
문제마다 다 맞아야 실전 대비가 완벽해 — 실무에서도 자주 터지는 실수니까.
import java.util.*;
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class TestHash {
public static void main(String[] args) {
Set<Student> set = new HashSet<>();
set.add(new Student("Jane", 20));
set.add(new Student("Jane", 20));
System.out.println(set.size());
}
}
A. 0
B. 1
C. 2
D. 예외 발생
지금 이 타이밍에 무슨 일이 벌어지는가?
new Student("Jane", 20) 두 개 생성 → 이건 다른 객체임
HashSet은 내부적으로 hashCode()와 equals()로 중복 판정함
Student 클래스에는 equals()와 hashCode()가 정의되지 않았음
따라서:
hashCode()는 Object.hashCode() → 주소 기반 → 다름equals()도 Object.equals() → 주소 기준 → 다름그래서 set에 중복이 아니라고 판단됨!
✅ 정답: C. 2
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student s = (Student) o;
return age == s.age && Objects.equals(name, s.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
Set<String> set = new HashSet<>();
String s1 = new String("apple");
String s2 = new String("apple");
set.add(s1);
System.out.println(set.contains(s2));
A. true
B. false
C. 예외
D. 컴파일 에러
s1과 s2는 new String("apple") → 다른 객체
하지만 String 클래스는 이미 equals()와 hashCode()가 잘 구현되어 있음
→ "apple" 두 객체는 동등하다고 판정됨
→ contains(s2) 는 true
✅ 정답: A. true
import java.util.*;
public class Trap {
public static void main(String[] args) {
Map<List<String>, String> map = new HashMap<>();
List<String> key = new ArrayList<>();
key.add("A");
map.put(key, "첫번째");
key.add("B");
System.out.println(map.get(key));
}
}
A. 첫번째
B. null
C. 예외
D. A, B
HashMap은 key.hashCode()로 위치를 정함key.add("B") → key 객체가 변경됨그래서 map.get(key) → 못 찾음 → null
✅ 정답: B. null
import java.util.*;
class Person implements Comparable<Person> {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
}
public class TreeSetTrap {
public static void main(String[] args) {
Set<Person> set = new TreeSet<>();
set.add(new Person("Kim", 20));
set.add(new Person("Lee", 20));
System.out.println(set.size());
}
}
A. 1
B. 2
C. 예외 발생
D. 컴파일 에러
TreeSet은 중복을 compareTo() 결과로 판단함this.age - o.age == 0 → 동등하다고 판단compareTo() == 0✅ 정답: A. 1
| 문제 유형 | 핵심 개념 | 실수 포인트 |
|---|---|---|
| 사용자 정의 클래스 | equals + hashCode 필수 | 안하면 중복 체크 안 됨 |
| String | hashCode, equals 잘 정의됨 | 안전함 |
| mutable key | HashMap/HashSet에 위험 | 해시값이 바뀌면 못 찾음 |
| TreeSet | compareTo()로 중복 판정 | equals 무시됨 |
좋아, 이제 해시 구조의 진짜 트랩 문제들 중에서도 실무와 정보처리기사 실기에서 정말 많이 출제되거나 헷갈리는 두 유형을 완전히 잡아줄게.
LinkedHashMap, EnumMap, IdentityHashMap 차이 문제다음 코드의 출력 결과는?
import java.util.*;
enum Day { MON, TUE, WED }
public class TestEnumMap {
public static void main(String[] args) {
Map<Day, String> map = new EnumMap<>(Day.class);
map.put(Day.MON, "월요일");
map.put(Day.TUE, "화요일");
for (Map.Entry<Day, String> entry : map.entrySet()) {
System.out.print(entry.getKey() + " ");
}
}
}
A. MON TUE
B. TUE MON
C. 무작위 순
D. 예외 발생
EnumMap은 enum 전용 해시맵이고, 내부적으로 배열로 관리enum의 정의 순서(MON, TUE, WED)를 따름✅ 정답: A. MON TUE
import java.util.*;
public class TestLinkedMap {
public static void main(String[] args) {
Map<String, String> map = new LinkedHashMap<>();
map.put("B", "banana");
map.put("A", "apple");
for (String key : map.keySet()) {
System.out.print(key + " ");
}
}
}
A. A B
B. B A
C. 무작위
D. 예외 발생
LinkedHashMap은 삽입 순서를 유지함✅ 정답: B. B A
import java.util.*;
public class IdentityMapTest {
public static void main(String[] args) {
Map<String, String> map = new IdentityHashMap<>();
String a = new String("hello");
String b = new String("hello");
map.put(a, "first");
map.put(b, "second");
System.out.println(map.size());
}
}
A. 1
B. 2
C. 예외
D. 0
IdentityHashMap은 key 비교에 equals()가 아니라 == (주소 비교) 사용new String("hello") → a, b는 서로 다른 주소→ 그래서 두 개 다 다른 key로 처리됨
✅ 정답: B. 2
| 자료구조 | 순서 유지 | 정렬 | 비교 방식 | 특징 |
|---|---|---|---|---|
HashMap | ❌ | ❌ | equals() + hashCode() | 기본 |
LinkedHashMap | ✅ (삽입순서) | ❌ | equals() + hashCode() | 순서 보장 |
EnumMap | ✅ (enum 순서) | ❌ | enum ordinal | 빠르고 메모리 효율 |
IdentityHashMap | ❌ | ❌ | == (주소 비교) | 매우 특수한 상황 (성능, 메모리 절약용) |
Map.containsKey() vs == 비교 실수 문제import java.util.*;
public class KeyTrap {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
String a = new String("hi");
String b = new String("hi");
map.put(a, 1);
if (map.containsKey(b)) {
System.out.println("FOUND");
} else {
System.out.println("NOT FOUND");
}
}
}
A. FOUND
B. NOT FOUND
C. 예외
D. 컴파일 에러
a와 b는 new String("hi") → 주소는 다르지만,String.equals()는 값이 같으면 true, hashCode()도 같음→ containsKey()는 equals()와 hashCode() 기준으로 비교
→ 동등한 key로 인식함
✅ 정답: A. FOUND
String key1 = "hi";
String key2 = "hi";
System.out.println(key1 == key2); // ?
A. true
B. false
C. 예외
"hi"는 상수 풀(Constant Pool) 에 저장됨✅ 정답: A. true
💣 이게 new String("hi") 와 다르다는 게 핵심
| 비교 대상 | 사용 방식 | true 조건 |
|---|---|---|
== | 주소 비교 | 같은 객체일 때만 true |
equals() | 값 비교 | 값이 같으면 true |
HashMap.containsKey() | equals + hashCode 기준 | 두 조건 모두 만족해야 true |
IdentityHashMap.containsKey() | == 기준 | 주소 같아야 true |