| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- HashMap
- 개발자팁
- 코딩테스트
- 코딩공부
- 개발자취업
- 자바프로그래밍
- 백준
- 멀티스레드
- 자료구조
- 자바
- 자바개발
- 코딩테스트팁
- 정렬
- 프로그래머스
- JVM
- 코딩인터뷰
- 가비지컬렉션
- 개발공부
- 파이썬
- 클린코드
- 자바기초
- 메모리관리
- 알고리즘공부
- 객체지향
- 예외처리
- 프로그래밍기초
- 자바공부
- 코딩테스트준비
- 알고리즘
- Java
- Today
- Total
코드 한 줄의 기록
자바 HashMap과 HashSet, 실무에서 제대로 활용하기 - 성능까지 고려한 완벽 가이드 본문
HashMap과 HashSet의 핵심 차이점
HashMap은 키-값 쌍으로 데이터를 저장하는 Map 인터페이스의 구현체이고, HashSet은 중복을 허용하지 않는 Set 인터페이스의 구현체입니다. 흥미롭게도 HashSet은 내부적으로 HashMap을 사용하여 구현되어 있어, 추가되는 값을 키로 사용하고 더미 객체를 값으로 저장합니다.
내부 구조와 작동 원리
HashMap의 내부 동작
HashMap은 해시 테이블을 기반으로 하며, 배열과 연결 리스트(또는 트리)의 조합으로 구현됩니다. 키의 해시코드를 계산하여 배열 인덱스를 결정하고, 해시 충돌 시 연결 리스트나 균형 트리를 사용하여 데이터를 저장합니다.
Java 8의 성능 개선 - 트리화(Treeification)
Java 8부터는 해시 충돌이 빈번한 버킷에 대해 트리화 기능을 도입했습니다. 하나의 버킷에 8개 이상의 엔트리가 충돌하면 연결 리스트를 균형 트리로 변환하여 최악의 경우 O(n)이던 성능을 O(log n)으로 개선합니다.
// TREEIFY_THRESHOLD = 8
// 8개 이상 충돌 시 연결 리스트 → 균형 트리 변환
이 개선으로 Java 8에서는 대용량 데이터 처리 시 극적인 성능 향상을 보여줍니다. 실제 벤치마크 결과, 100만 개 레코드 처리 시 Java 7 대비 약 87배 빠른 성능을 보였습니다.
성능 및 메모리 비교
시간 복잡도
- 검색/추가/삭제: 둘 다 평균 O(1), 최악 O(log n) (Java 8+)
- 성능: HashMap이 HashSet보다 약간 더 빠름
메모리 사용량
HashSet은 HashMap과 동일한 메모리를 사용합니다. SIZE개의 값을 저장하기 위해 32 * SIZE + 4 * CAPACITY 바이트가 필요하며, 이는 각 엔트리마다 키와 더미 값 객체를 모두 저장하기 때문입니다.
실무 활용 케이스
HashMap 최적 사용 사례
// 사용자 정보 캐싱
HashMap<Long, User> userCache = new HashMap<>();
HashMap<String, Long> emailToIdMap = new HashMap<>();
// 단어 빈도 분석
HashMap<String, Integer> wordCount = new HashMap<>();
for (String word : words) {
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
데이터베이스 레코드 매핑, 학생 번호와 학생 객체 매핑, 카테고리별 상품 목록 정리 등이 있습니다.
HashSet 최적 사용 사례
// 중복 데이터 필터링
HashSet<String> uniqueItems = new HashSet<>(duplicatedList);
- 블랙리스트 확인
- 권한 체크
- 처리된 데이터 여부 확인
또한 집합 연산, 고유한 방문자 수 계산 등에 사용됩니다.
성능 최적화 팁
초기 용량 설정
// 예상 크기의 1.33배로 설정하여 리해싱 방지
int expectedSize = 1000;
int initialCapacity = (int) (expectedSize / 0.75) + 1;
HashMap<String, String> optimizedMap = new HashMap<>(initialCapacity);
적절한 Load Factor 사용
기본 Load Factor 0.75는 시간과 공간의 균형을 잘 맞춘 값입니다. 메모리가 중요하면 더 높게, 속도가 중요하면 더 낮게 설정할 수 있습니다.
hashCode()와 equals() 최적화
@Override
public int hashCode() {
return Objects.hash(field1, field2);
}
@Override
public boolean equals(Object obj) {
// 적절한 equals 구현
}
선택 가이드라인
HashMap을 선택해야 하는 경우
- 키를 통한 빠른 값 검색이 필요할 때
- 카운팅이나 집계 작업
- 캐싱 시스템 구현
- 키-값 연관 관계가 중요할 때
HashSet을 선택해야 하는 경우
- 중복 제거가 주 목적일 때
- 멤버십 테스트가 빈번할 때
- 단순히 유일한 값들의 집합이 필요할 때
- 집합 연산(교집합, 합집합 등)이 필요할 때
주의사항
스레드 안전성
HashMap과 HashSet 모두 스레드 안전하지 않습니다. 멀티스레드 환경에서는 ConcurrentHashMap이나 Collections.synchronizedMap()을 사용해야 합니다.
null 값 처리
- HashMap: null 키 1개, null 값 여러 개 허용
- HashSet: null 값 1개만 허용
반복 중 수정 금지
반복 중에 컬렉션을 수정하면 ConcurrentModificationException이 발생하므로, Iterator의 remove() 메서드를 사용해야 합니다.
자바 String 문자열의 불변성(immutable) 이해하기: 완벽 가이드
자바를 공부하다 보면 가장 먼저 마주하게 되는 흥미로운 개념 중 하나가 바로 String의 불변성(Immutable)입니다. 많은 개발자들이 "분명히 문자열을 변경했는데 왜 불변이라고 하지?"라는 의문을
byteandbit.tistory.com
'JAVA' 카테고리의 다른 글
| Java 개발자를 위한 필수 가이드: main 메소드 의존 습관을 버리고 깔끔한 코드로 나아가는 길 (0) | 2025.09.11 |
|---|---|
| 자바 변수 초기화와 스코프(Scope) 완벽 정리: 실무에서 놓치기 쉬운 함정들 (0) | 2025.09.10 |
| 자바 개발자가 꼭 알아야 할 핵심 메서드 활용 가이드: System.out.println(), equals(), hashCode(), toStr (0) | 2025.09.09 |
| 자바 String 문자열의 불변성(immutable) 이해하기: 완벽 가이드 (0) | 2025.09.07 |
| Java 기본 자료형과 객체형 변수 완전 정복: 선언부터 초기화까지 실전 가이드 (0) | 2025.09.06 |