LinkedHashMap을 이용한 LRUCache의 구현
LRUCache algorithms
LRUCache는 (Least-Recently-Used Cache)의 약자이다. 최근에 가장 적게 사용된 item을 Cache에서 제거하는 알고리즘이다. 참조 문서 : http://en.wikipedia.org/wiki/Cache_algorithms
LinkedHashMap VS HashMap
- HashMap
- 내부의 Entry 구현을 보면 collision(충돌)이 발생했을 경우에만 Entry의 next 변수를 이용해서 linked list를 이용하도록 구현했다.
- HashMap에 item을 넣는 put 메소드
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry 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++;
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry e = table[bucketIndex];
table[bucketIndex] = new Entry(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
- LinkedHashMap
- HashMap을 상속받아서 구현한 클래스이다. 내부의 Entry 구현을 보면 기본적으로 Entry에서 before, after 변수를 가지고 있어서 Entry를 linked list 구조로 별도 연결을 하고 있다.
void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed,
// else grow capacity if appropriate
Entry eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
}
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry old = table[bucketIndex];
Entry e = new Entry(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
LinkedHashMap을 생성하는 시점에 accessOrder값을 true로 설정해주어야 한다. true로 설정하면 access할 때마 linkedlist의 맨 앞으로 이동시키는 로직이 동작하고, false로 설정하거나 설정하지 않으면 삽입 순서대로 linked list가 생성되므로 LRU 알고리즘으로 동작하지 않는다.
public V get(Object key) {
Entry e = (Entry)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
private void remove() {
before.after = after;
after.before = before;
}
private void addBefore(Entry existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
void recordAccess(HashMap m) {
LinkedHashMap lm = (LinkedHashMap)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
LinkedHashMap 클래스는 removeEldestEntry 메소드를 가지고 있어서 가장 오래된 Entry를 삭제할 수 있는 기능이 있다. 하지만 소스를 보면 메소드 안에서 실제 Entry를 삭제하는 코드는 없는 것을 발견할 수 있다. 무조건 false 를 반환하도록 되어 있다.
해당 메소드를 overriding해서 삭제 여부를 boolean 값으로 반환할 뿐임
protected boolean removeEldestEntry(Map.Entry eldest) {
return false;
}
- LinkedHashMap을 이용한 LRUCache의 구현
- LinkedHashMap의 removeEldestEntry의 메소드 안에는 실제 Entry를 삭제하는 코드가 없으므로 해당 메소드를 overriding해야 한다. 메소드 안에서 Map의 크기가 cache의 사이즈보다 커지면 삭제하라는 true값을 반환하도록 구현하면 LinkedHashMap에서 알아서 가장 오래된 Entry값을 삭제한다.
- © boscolyu
- Theme: Comfusion LLC