HashMap은 key와 value로 구성된 Map 데이터를 해싱함수를 통해 산출된 인덱스 위치의 버킷(배열)안에 저장하는 자료구조.
해싱함수(function)는 key 값(X)을 받아 인덱스(Y)를 산출함.
key값을 알면 바로 value의 값을 도출해낼 수 있어 시간 복잡도가 O(1)으로 빠른 성능을 갖고 있음.
✔ HashMap은 순서를 보장하지 않음
✔ 정렬이 필요하다면 별도의 조치가 필요함
참조 : https://velog.io/@seha01130/JAVA-HashMap-정렬하기
HashMap은 버킷에 Map 데이터를 index 순서대로 저장하지 않음. 따라서 for문이나 while 문으로 탐색하지 못하므로 탐색에 필요한 모듈인 iterator()를 사용하여 탐색한다.
KeySet, Values, EntrySet (데이터 반환 참고) 모두 iterator 사용 가능.
Iterator의 시간복잡도
요소의 개수를 n이라 하면, 모든 요소를 한 번씩 방문하는 데 O(n)
iterator.next() 한 번 호출할 때 평균 O(1), 최악 O(n)
그러나 보통 HashMap은 해시 충돌이 적게 발생하도록 설계되므로, Iterator의 성능은 평균적으로 O(n) 순회와 O(1) 탐색이 보장됨.
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class tmp {
public static void main(String[] args) {
HashMap<String, String> hshMap = new HashMap<>();
hshMap.put("헬로키티", "고양이");
hshMap.put("케로로", "개구리");
hshMap.put("마이멜로디", "토끼");
hshMap.put("한교동", "물고기");
hshMap.put("포챠코", "강아지");
//Set<Map.Entry<String, String>> entrySet = hshMap.entrySet();
//Iterator<Map.Entry<String, String>> itr = entrySet.iterator();
//위의 두 작업을 아래처럼 한 번에 쓸 수 있다.
Iterator<Map.Entry<String, String>> itr = hshMap.entrySet().iterator();
System.out.println();
System.out.println("[출력]");
while (itr.hasNext()) {
Map.Entry<String, String> eachEntry = itr.next();
System.out.printf("[%s] %s\n", eachEntry.getValue(), eachEntry.getKey());
}
System.out.println();
System.out.println("[동물]");
Collection<String> values = hshMap.values();
Iterator<String> iterator = values.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println();
System.out.println("[캐릭터 종류]");
Set<String> keys = hshMap.keySet();
iterator = keys.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

그러나 만약 한 번 iterator를 사용하여 탐색을 끝마쳤다면 다시 해당 반복자 객체를 사용하여 탐색할 수 없다. 즉, iterator를 재사용하려면 새로 반복자 객체 하나를 다시 생성하여 사용하여야 함.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class HashMapIteratorExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
// 첫 번째 순회
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 재사용 시도 (불가능)
System.out.println("다시 순회 시도...");
while (iterator.hasNext()) {
System.out.println(iterator.next()); // 출력 없음 (이미 끝까지 탐색됨)
}
// 새로 Iterator를 생성하면 가능
iterator = map.entrySet().iterator();
System.out.println("새로운 Iterator 생성 후 순회...");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

HashMap<String, Integer> map1 = new HashMap<>();
map1.put("apple", 100);
map1.put("banana", 150);
HashMap<String, Integer> map2 = new HashMap<>();
map2.put("orange", 200);
map2.put("banana", 180); // 기존 키 'banana'가 덮어짐
map1.putAll(map2);
System.out.println("map1 after putAll: " + map1);
// {apple=100, banana=180, orange=200}
HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 100);
map.putIfAbsent("banana", 150); // banana가 없으므로 저장됨
map.putIfAbsent("apple", 200); // apple이 있으므로 무시됨
System.out.println("map: " + map);
// {apple=100, banana=150}
hm.put(word, hm.getOrDefault(word, 0) + 1);