자바 개발에서 HashMap은 가장 기본이면서도, 실무에서는 의외로 많은 문제가 발생하는 컬렉션입니다.
간단한 예제에서는 잘 동작하지만, 실제 서비스 코드에서는 “값이 왜 바뀌었는지 모르겠다”, “null 때문에 갑자기 장애가 났다” 같은 상황을 한 번쯤은 반드시 겪게 됩니다.
이 글에서는 HashMap의 기본 개념부터 주요 메서드, 그리고 실무에서 꼭 알아야 할 주의사항까지 정리해보도록 하겠습니다.
01. HashMap이란?
HashMap은 데이터를 키(Key)와 값(Value)의 쌍으로 저장하는 Map 인터페이스의 대표적인 구현체입니다. 리스트처럼 순서로 접근하지 않고, 키를 통해 바로 값에 접근하는 구조를 가지고 있습니다.
내부적으로는 key의 hashCode() 값을 이용해 저장 위치를 계산하기 때문에 데이터 조회, 추가, 삭제가 평균적으로 매우 빠릅니다.
- Key 값은 중복 불가
- Value 값은 중복 가능
- 저장 순서 보장되지 않음
실무에서 HashMap을 안전하게 사용하려면 Key 객체의 hashCode()와 equals() 구현이 성능과 안정성에 직접적인 영향을 준다는 점을 반드시 이해해야 합니다.
02. HashMap 생성방법
HashMap<String, String> map1 = new HashMap<>(); // 기본 capacity 16, load factor 0.75
HashMap<String, String> map2 = new HashMap<>(20); // capacity 20
HashMap<String, String> map3 = new HashMap<>(20, 0.8f); // capacity 20, load factor 0.8
HashMap<String, String> map4 = new HashMap<>(map1); // 다른 Map으로 초기화
- capacity : 데이터 저장 용량
- load factor : 부하계수로, 저장공간을 추가로 확보해야 하는 시점을 지정
(load factor 0.8은 저장공간이 80% 채워질 경우 추가로 저장공간을 확보함)
대량 데이터가 예상되는 경우에는 초기 용량을 지정하여 사용하는 것이 리사이징 비용을 줄이는 데 유리합니다.
03. HashMap 주요 메서드 정리
1) 데이터 추가
- V put(K key, V value) : key와 value 저장 (기존 데이터에 key가 이미 존재하면 덮어씀)
- V putIfAbsent(K key, V value) : 기존 데이터에 해당 key가 없을때만 key와 value 저장
- void putAll(Map<? extends K, ? extends V> m) : Map m의 데이터를 전부 저장
가장 자주 발생하는 문제 중 하나는 기존 값이 있는지 모르고 put()을 호출해 의도지 않게 데이터가 변경되는 경우입니다.
put()은 해당 key가 이미 존재하면 데이터를 덮어쓰고, 이전 데이터의 value값을 반환합니다. 해당 key가 없다면 데이터를 저장하고 null이 반환됩니다. 만약 새로운 key 데이터만 저장하고자 한다면 putIfAbsent() 이 더 안전합니다.
2) 데이터 조회
- V get(Object key) : key와 맵핑된 value값 반환 (존재하지 않으면 null 반환)
- V getOrDefault(Object key, V defaultValue) : key와 맵핑된 value값을 반환하고 없으면 defaultValue값을 반환합니다.
- Set<Map.Entry<K, V>> entrySet( ) : 모든 key-value 맵핑 데이터를 가진 Set 데이터를 반환합니다.
- Set<K> keySet( ) : 모든 key 값을 가진 Set 데이터를 반환합니다.
- Collection<V> values( ) : 모든 value 값을 가진 Collection 데이터를 반환합니다.
null 체크로 인한 오류를 줄이기 위해서는 getOrDefault()가 자주 사용됩니다.
3) 데이터 삭제
- V remove(Object key) : key와 일치하는 데이터( key와 value)를 삭제합니다. (삭제된 value값 반환)
- boolean remove(Object key, Object value) : key와 value가 동시에 일치하는 데이터를 삭제합니다.
- void clear( ) : 모든 데이터를 삭제합니다.
4) 데이터 수정
- V replace(K key, V value) : 해당 key의 value값 변경
- V replace(K key, V oldValue, V newValue) : 해당 key와 oldValue값이 동시에 일치때 newValue로 변경
5) 자주 사용하는 기타 메서드
- boolean containsKey(Object key) : key 존재 여부 확인 (put 전에 조건 체크 용도로 자주 사용)
- boolean containsValue(Object value) : value 존재 여부 확인 (빈도는 낮지만 디버깅 시 유용)
- boolean isEmpty() : 데이터가 비어 있는지 확인
- int size() : 데이터 개수 반환
04. 사용예제
1) HashMap 생성 및 실행
public class HelloWorld {
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<>();
HashMap<String, String> h2 = new HashMap<>();
h1.put("aaa", "1111");
h1.put("bbb", "2222");
h1.put("ccc", "3333");
h1.putIfAbsent("aaa", "0000");
h1.putIfAbsent("ddd", "4444");
h2.putAll(h1);
System.out.println("h1 : " + h1);
System.out.println("h2 : " + h2);
System.out.println("[1]: " + h1.containsKey("aaa"));
System.out.println("[2]: " + h1.containsValue("1111"));
System.out.println("[3]: " + h1.isEmpty());
System.out.println("[4]: " + h1.size());
System.out.println("[5]: " + h1);
System.out.println("[6]: " + h1.remove("aaa", "1111"));
System.out.println("[7]: " + h1.put("bbb", "0000"));
System.out.println("[8]: " + h1.replace("ccc", "0000"));
System.out.println("h1 : " + h1);
System.out.println("h2 : " + h2);
for (String key: h1.keySet()) {
String value = h1.get(key);
System.out.println("Key:" + key + ", Value:" + value);
}
}
}
2) 실행결과
h1 : {aaa=1111, ccc=3333, bbb=2222, ddd=4444}
h2 : {aaa=1111, ccc=3333, bbb=2222, ddd=4444}
[1]: true
[2]: true
[3]: false
[4]: 4
[5]: {aaa=1111, ccc=3333, bbb=2222, ddd=4444}
[6]: true
[7]: 2222
[8]: 3333
h1 : {ccc=0000, bbb=0000, ddd=4444}
h2 : {aaa=1111, ccc=3333, bbb=2222, ddd=4444}
Key:ccc, Value:0000
Key:bbb, Value:0000
Key:ddd, Value:4444
05. 실무에서 꼭 알아야 할 주의사항
- Key 객체는 equals()와 hashCode()를 반드시 올바르게 구현할 것! (중요) - 아래 링크 참고
: 가급적 Key객체는 수정이 불가능한 Immutable 객체로 설계하는 것이 안전합니다. - 순서 보장이 필요하다면 LinkedHashMap을 선택할 것!
: HashMap은 삽입 순서를 전혀 보장하지 않습니다. - 멀티스레드 환경에서는 ConcurrentHashMap 사용할 것! (중요) - 아래 링크 참고
: HashMap은 Thread-safe하지 않습니다. 여러 스레드가 동시에 접근할 경우 데이터 유실이나 에러가 발생할 수 있습니다. - get()의 null 반환 가능성을 항상 고려할 것!
: 찾으려는 Key가 없을때 HashMap은 null을 반환하므로 NPE(NullPointerException)가 발생할 가능성이 있습니다. 이때에는 getOrDefault() 활용을 고려해 보세요 - 초기 용량(initial Capacity) 설정을 신중히 고려할 것!
: 데이터가 늘어날수록 배열 크기를 키우는 리사이징(Resizing) 작업이 계속 발생합니다. 리사이징 횟수를 최소화 할 수 있도록 담을 데이터의 양을 고려하여 초기 용량을 설정하세요
HashMap은 매우 편리하지만, 내부 구조와 특성을 이해하지 못하면 디버깅이 어려운 문제를 만들기 쉽습니다.
이 글의 기준만 지켜도 대부분의 HashMap 관련 실무 문제는 충분히 예방할 수 있습니다.
cf) 위 주의사항 중 추가 설명이 필요한 내용을 정리하였습니다. (중요한 내용이니 꼭 한번 살펴보세요!)
[Java] HashMap Key로 객체를 사용할 때 꼭 알아야 할 equals()와 hashCode()
HashMap을 어느 정도 써봤다면 한 번쯤 이런 경험이 있을 겁니다.“분명 put으로 값을 넣었는데, get을 하면 null이 나온다”, “같은 객체인데 key로 인식이 안 된다” 같은 상황 말이죠.이 문제의 거
kadosholy.tistory.com
[Java] HashMap vs ConcurrentHashMap 차이점 총정리 (언제 무엇을 써야 할까?)
자바에서 가장 많이 사용하는 자료구조 중 하나가 바로 Map입니다. 그중에서도 HashMap과 ConcurrentHashMap은 면접 단골 질문이자, 성능에 직결되는 아주 중요한 주제입니다. 이번에는 두 자료구조의
kadosholy.tistory.com
'Java' 카테고리의 다른 글
| [Java] HashMap vs ConcurrentHashMap 차이점 총정리 (언제 무엇을 써야 할까?) (0) | 2026.01.29 |
|---|---|
| [Java] HashMap Key로 객체를 사용할 때 꼭 알아야 할 equals()와 hashCode() (0) | 2026.01.29 |
| [Java] 자바 설치 및 환경변수 등록하기 (OpenJDK 버전별 다운로드) (1) | 2025.08.02 |
| [Java] 자바 - 멀티채팅 프로그램 구현하기 (0) | 2022.08.10 |
| [Java] 자바 - 소켓통신이란? 소켓(Socket) 개념과 사용방법 (3) | 2022.08.08 |
