Hashtable
package java.util
해시 테이블의 구현체
HashMap과 유사하지만 동기화 지원
Hashing
키를 해시 함수를 사용해 변환한 해시 값 -> 배열의 인덱스로 사용하여 값을 저장
Key -> [ Hash Function ] -> Hash Value
Hash Table
(Key, Value) 형태의 데이터를 저장하는 자료구조
1. null을 제외한 모든 object를 key, value로 사용할 수 있다.
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode(); // key 객체의 해시코드 참조
...
}
2. Key로 사용되는 객체는 hashCode()와 equals() 메서드를 구현해야 한다.
- hashCode(): 객체의 주소 값에 해시 함수를 적용한 고유 값을 반환
- Hashtable의 인스턴스는 해시 테이블 성능에 영향을 미치는 두 가지 파라미터를 가진다.
=> initialCapacity, loadFactor
생성자
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
}
if (initialCapacity==0) {
initialCapacity = 1;
}
this.loadFactor = loadFactor;
table = new Hashtable.Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
- Capacity(용량): 해시 테이블의 버킷 수
- loadFactor(적재율): 해시 테이블의 용량이 자동으로 증가하기 전에 어느 정도의 용량을 허용하는지에 대한 기준
테이블 크기(N)에 대한 키의 개수(K) => K/N
Hashtable vs HashMap
- Hashtable은 key, value로 null을 사용할 수 없지만, HashMap은 null 사용 가능하다.
- Hashtable은 Dictionary 클래스를 상속하고 HashMap은 AbstractMap 클래스를 상속한다.
- Hashtable은 동기화를 지원하므로 HashMap보다 느리다
- 대부분의 메서드가 synchronized 키워드로 정의되어 있다. (thread-safe)
WeakHashMap
package java.util
Weak 키를 가진 해시 테이블
- WeakHashMap에 저장된 (Key, Value) 엔트리가 있을 때, Key가 사용되지 않으면 자동적으로 삭제된다.
- 정확하게는, Key 객체가 가비지 컬렉터에 의해 제거 -> Map에서 해당 Key에 매핑되는 Entry 자동 삭제
가비지 컬렉터: 힙(Heap) 내 객체 중 가비지를 찾아내 메모리 회수
가비지인지 판별하는 기준: 참조 여부(Reachability)
참조 유형과 가비지 컬렉션
참조 유형에 따라 GC 때의 동작을 다르게 지정
https://d2.naver.com/helloworld/329631
1. 강한 참조 (Strong Reference)
Object obj = new Object();
변수가 객체를 직접 참조할 때, 가비지 컬렉션의 대상이 되지 않는다.
2. 소프트 참조 (Soft Reference)
SoftReference<Object> softReference = new SoftReference<>(new Object());
SoftReference 클래스를 사용해 만들어진다.
메모리가 부족할 때 가비지 컬렉터에 의해 회수된다.
3. 약한 참조 (Weak Reference)
WeakReference<Object> weakReference = new WeakReference<>(new Object());
WeakReference 클래스를 사용해 만들어진다.
가비지 컬렉션 시점에 항상 회수된다.
내부적으로 WeakReference<Object>를 상속받은 Entry를 static class로 정의하여 key-value 저장
import java.util.WeakHashMap;
public class Main {
public static void main(String[] args) {
WeakHashMap<String, String> map = new WeakHashMap<>();
String key1 = new String("1000");
String key2 = new String("2000");
map.put(key1, "A");
map.put(key2, "B");
key1 = null; // 강한 참조 삭제
System.gc(); // 강제 가비지 콜렉션. key1이 참조하던 인스턴스 제거
map.entrySet().forEach(System.out::println); // map에 저장된 Entry 출력
// 출력 결과
// 2000=B
}
}
- key, value로 null 사용 가능
- HashMap 클래스와 유사한 특성을 가지며, initialCapacity, loadFactor으로 성능 제어
- String 리터럴처럼 캐싱되어 있는 객체는 WeakHashMap 은 자동으로 삭제되지 않는다.
EnumMap
package java.util
Enum 타입 키를 사용하는데 특화된 Map 구현체
- 내부적으로 배열(Array)을 사용하여 데이터를 저장한다. -> 성능 이점
- 해시를 사용하지 않으므로 해시 충돌이 없다.
- null 키를 허용하지 않는다.
- enum 상수가 선언되는 순서인 natural order에 따라 Key 순서를 유지한다.
-> keySet(), entrySet(), values() 등으로 element를 순차 탐색하면 순서 확인 가능
enum DayOfWeek {
MON, TUE, WED, THU, FRI, SAT, SUN
}
public class Main {
public static void main(String[] args) {
EnumMap<DayOfWeek, String> enumMap = new EnumMap<>(DayOfWeek.class);
enumMap.put(DayOfWeek.SUN, DayOfWeek.SUN.name());
enumMap.put(DayOfWeek.FRI, DayOfWeek.FRI.name());
enumMap.put(DayOfWeek.MON, DayOfWeek.MON.name());
for(DayOfWeek key : enumMap.keySet()) {
System.out.println(key);
}
// 출력 결과
// MON
// FRI
// SUN
}
}
put()
enum 상수를 인덱스로 배열에 value 저장
중복 키로 입력 시 이전 값 삭제
public V put(K key, V value) {
typeCheck(key);
// enum 상수를 인덱스로 배열에 value 저장
int index = key.ordinal();
Object oldValue = vals[index];
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}
생성자
1. Key 의 Enum 타입을 지정하여 빈 EnumMap 생성
- keyUniverse: 캐싱을 위한 키 배열
public EnumMap(Class<K> keyType) {
this.keyType = keyType;
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
2. EnumMap으로부터 같은 Key 타입을 가진 EnumMap 생성
public EnumMap(EnumMap<K, ? extends V> m) {
keyType = m.keyType;
keyUniverse = m.keyUniverse;
vals = m.vals.clone();
size = m.size;
}
3. Map 객체로부터 EnumMap 생성
public EnumMap(Map<K, ? extends V> m) {
if (m instanceof EnumMap) {
EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
keyType = em.keyType;
keyUniverse = em.keyUniverse;
vals = em.vals.clone();
size = em.size;
} else {
if (m.isEmpty())
throw new IllegalArgumentException("Specified map is empty");
keyType = m.keySet().iterator().next().getDeclaringClass();
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
putAll(m);
}
}