π ν΄μλ 무μμΈμ§, ν΄μ μΆ©λμ μ μΌμ΄λκ³ μ΄λ»κ² λ°©μ§ν μ μλμ§ μκ³ μμ΄μΌ νλ€. μ λͺ¨λ₯Έλ€λ©΄ μ΄ κΈμ μ½κ³ λμμ€λ κ²μ μΆμ²νλ€.
Javaμ HashMapμμ μ¬μ©νλ λ°©μμ Separate Chainingμ΄λ€. Open Addressing λμ Separate Chainingμ μ¬μ©νλ μ΄μ λ ν¬κ² λ κ°μ§μ΄λ€.
Java 7κ³Ό Java 8μμ HashMapμ΄ μ΄λ»κ² ꡬνλμλμ§ μ΄ν΄λ³΄μ.
μλλ Java 7 HashMapμ put() λ©μλ ꡬνλΆμ΄λ€.
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold); // table λ°°μ΄ μμ±
}
if (key == null) // nullμ ν€λ‘ μ¬μ©ν κ²½μ°
return putForNullKey(value);
// hashCode()λ₯Ό μ¬μ©νλ κ²μ΄ μλλΌ λ³΄μ‘° ν΄μ ν¨μλ₯Ό μ΄μ©νλ λ³νλ ν΄μ ν¨μλ₯Ό μ¬μ©νλ€.
int hash = hash(key);
// i κ°μ΄ ν΄μ λ²ν·μ μΈλ±μ€μ΄λ€.
// indexFor()λ hash % table.lengthμ κ°μ μλμ λ©μλμ΄λ€.
int i = indexFor(hash, table.length);
// ν΄μ λ²ν·μ μλ Linked Listλ₯Ό μννλ€.
// λ§μ½ κ°μ ν€κ° μ΄λ―Έ μ μ₯λμ΄ μλ€λ©΄ κ΅μ²΄νλ€.
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
// λ²ν·μ λ§μ§λ§ Entry μ°Έμ‘°κ°, key, value, hashλ₯Ό μ μ₯νλ μλ‘μ΄ Entryλ₯Ό μμ±ν΄ μ μ₯νλ€.
addEntry(hash, key, value, i);
return null;
}
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
// ...
}
μ¬κΈ°μ λͺ κ°μ§ μ€μν λΆλΆμ μ§κ³ λμ΄κ°λ©΄,
put() λ©μλλ₯Ό μΆ©λΆν μ΄ν΄νλ€λ©΄ μλ μ½λλ₯Ό μ€νμμΌ°μ λ μ΄λ€ κ²°κ³Όκ° λμ¬μ§ μμΈ‘ν΄λ³΄μ.
public static void main(String[] args) {
HashMap<Book, String> map = new HashMap<>();
Book book1 = new Book(1, "μ€λΈμ νΈ");
Book book2 = new Book(1, "κ°μ²΄ μ§ν₯μ μ¬μ€κ³Ό μ€ν΄");
Book book3 = new Book(2, "ν λΉμ μ€νλ§ Vol.1");
map.put(book1, "First Input");
map.put(book2, "Second Input");
map.put(book3, "Third Input");
System.out.println(map.get(book1));
System.out.println(map.get(book2));
}
static class Book {
private int category;
private String title;
public Book(int category, String title) {
this.category = category;
this.title = title;
}
@Override
public int hashCode() {
return category;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book key = (Book) o;
return category == key.category && Objects.equals(title, key.title);
}
}
Book ν΄λμ€μ hashCode()κ° category κ°μ 리ν΄νλλ‘ μ¬μ μνκΈ° λλ¬Έμ book1κ³Ό book2λ λμΌν ν΄μκ°μ κ°κ² λλ€. ν΄μ μΆ©λμ΄ λ°μνμ§λ§ λ κ°μ²΄λ λμΌν ν΄μ λ²ν·μ Linked List ννλ‘ μ μ₯λκΈ° λλ¬Έμ μλμ κ°μ΄ First Inputμ΄ λλ½λμ§ μκ³ μΆλ ₯λλ€.
Java 7κ³Ό λ¬λ¦¬ Java 8μμλ λ°μ΄ν°μ κ°μκ° λ§μμ§λ©΄ Linked List λμ Treeλ₯Ό μ¬μ©νλ€. μ€μ ν΄μ κ°μ κ· λ± λΆν¬κ° μλλ©°, κ· λ± λΆν¬λ₯Ό λ°λ₯Έλ€κ³ νλλΌλ birthday problemμ²λΌ μΌλΆ ν΄μ λ²ν· λͺ κ°μ λ°μ΄ν°κ° μ§μ€λ μ μλ€. λ°λΌμ, λ°μ΄ν°κ° μΌμ κ°μ μ΄μμΌ λ Linked List λμ Treeλ₯Ό μ¬μ©νλ©΄ μ±λ₯μ μ΄μ μ΄ μλ€. μ΄λ μ¬μ©νλ νΈλ¦¬λ Red-Black νΈλ¦¬μ΄λ€.
Linked Listλ₯Ό μ¬μ©ν μ§, Treeλ₯Ό μ¬μ©ν μ§λ νλμ ν΄μ λ²ν·μ ν λΉλ ν€-κ° μμ κ°μμ λ°λΌ λ¬λΌμ§λ€. Java 8μ νλμ ν΄μ λ²ν·μ 8κ°μ ν€-κ° μμ΄ λͺ¨μ΄λ©΄ Linked Listλ₯Ό Treeλ‘ λ³κ²½νλ€. λ§μ½ ν΄λΉ λ²ν·μ μλ λ°μ΄ν°λ₯Ό μμ νμ¬ κ°μκ° 6κ°μ μ΄λ₯΄λ©΄ λ€μ Linked Listλ‘ λ³κ²½νλ€
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
Java 8μμλ Entry ν΄λμ€ λμ Node ν΄λμ€λ₯Ό μ¬μ©νλ€. λν, Tree κ΅¬μ‘°λ‘ κ°μ μ μ₯ν μ μλλ‘ Nodeμ νμ ν΄λμ€μΈ TreeNodeκ° μ‘΄μ¬νλ€.
static class Node<K, V> implements Map.Entry<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;
Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
// ...
}
static final class TreeNode<K, V> extends LinkedHashMap.Entry<K, V> {
TreeNode<K, V> parent;
TreeNode<K, V> left;
TreeNode<K, V> right;
TreeNode<K, V> prev;
boolean red;
TreeNode(int hash, K key, V val, Node<K, V> next) {
super(hash, key, val, next);
}
// ...
}
ν΄μ λ²ν· κ°μμ κΈ°λ³Έκ°μ 16μ΄κ³ , λ°μ΄ν°μ κ°μκ° μκ³μ μ μ΄λ₯Ό λλ§λ€ ν΄μ λ²ν· κ°μμ ν¬κΈ°λ₯Ό λ λ°°μ© μ¦κ°μν¨λ€. μκ³μ μ νμ¬μ λ°μ΄ν° κ°μκ° (load factor x νμ¬μ ν΄μ λ²ν· κ°μ)μμ΄λ₯Ό λμ΄λ€. load factorμ κΈ°λ³Έκ°μ 0.75μ΄λ€
λ²ν· κ°μκ° λ λ°°λ‘ μ¦κ°ν λλ§λ€, λͺ¨λ ν€-κ° λ°μ΄ν°λ₯Ό μ½μ΄ μλ‘μ΄ Separate Chainingμ ꡬμ±ν΄μΌ νλ€. λ°λΌμ, HashMap κ°μ²΄μ μ μ₯λ λ°μ΄ν°μ κ°μκ° μ΄λ μ λμΈμ§ μμΈ‘ κ°λ₯ν κ²½μ°μλ μμ±μλ‘ λ²ν· κ°μλ₯Ό μ§μ ν΄ λΆνμν Separate Chaining μ¬κ΅¬μ±μ΄ μΌμ΄λμ§ μλλ‘ ν μ μλ€.
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
// μ ν΄μ λ²ν·μ μμ±ν λ€μ κΈ°μ‘΄ λ°μ΄ν°λ₯Ό μ ν΄μ λ²ν·μ μ μ₯νλ€.
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
// λͺ¨λ ν΄μ λ²ν·μ μλ Linked Listλ₯Ό μννλ€.
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
κ·Έλ¬λ, μ΄λ κ² ν΄μ λ²ν· ν¬κΈ°λ₯Ό 2λ°°λ‘ νμ₯νλ©΄ ν΄μ λ²ν·μ κ°μ Mμ΄ 2μ κ±°λμ κ³± ννκ° λκΈ° λλ¬Έμ ν΄μ λ²ν·μ μΈλ±μ€λ₯Ό X.hashCode() % Mλ‘ κ³μ°ν λ X.hashCode()μ νμ aκ°μ λΉνΈλ§ μ¬μ©νκ² λλ€.
μλ μμλ₯Ό 보μ. 2μ κ±°λμ κ³±μΌλ‘ λλ¨Έμ§ μ°μ°μ νλ©΄ μ§μλ§νΌμ νμ λΉνΈλ₯Ό μ¬μ©νλ€λ κ²μ μ μ μλ€.
// μ§μκ° 2μΌ λ νμ 2κ°μ λΉνΈλ§ μ¬μ©ν¨.
18 % 4 = 10010 % 2^2 = 10 = 2
20 % 4 = 10100 % 2^2 = 00 = 0
// μ§μκ° 3μΌ λ νμ 3κ°μ λΉνΈλ§ μ¬μ©ν¨.
25 % 8 = 11001 % 2^3 = 001 = 1
43 % 8 = 101011 % 2^3 = 011 = 3
μ¦, ν¨μκ° 32λΉνΈ μμμ κ³ λ₯΄κ² μ¬μ©νλλ‘ λ§λ€μλ€ νλλΌλ ν΄μ κ°μ 2μ κ±°λμ κ³±μΌλ‘ λλλ©΄ ν΄μ μΆ©λμ΄ μ½κ² λ°μν μ μλ€.
index = X.hashCode() % Mμ κ³μ°ν λ μ¬μ©νλ M κ°μ μμμΌ λ index κ° λΆν¬κ° κ°μ₯ κ· λ±ν μ μλ€. κ·Έλ¬λ M κ°μ΄ μμκ° μλκΈ° λλ¬Έμ λ³λμ 보쑰 ν΄μ ν¨μλ₯Ό μ΄μ©νμ¬ index κ° λΆν¬κ° κ°κΈμ κ· λ±ν μ μλλ‘ ν΄μΌ νλ€.
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// μμ λΉνΈμ κ°μ΄ ν΄μ λ²ν·μ μΈλ±μ€ κ°μ κ²°μ ν λ λ°μλ μ μλλ‘
// shift μ°μ°κ³Ό XOR μ°μ°μ μ¬μ©νλ€.
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Java 8μ 보쑰 ν΄μ ν¨μλ Java 7μ λΉν΄ κ½€λ λ¨μν ννμ 보쑰 ν΄μ ν¨μλ₯Ό μ¬μ©νλ€. κ·Έ μ΄μ λ λ κ°μ§μΈλ°