幾種常見型別Cache的實現
阿新 • • 發佈:2019-02-10
總述:Cache的實現有多種多樣,基本上各個java開源框架都有自己的實現,而使用的最多的三種Cache型別是:FIFO(先入先出)、Random(隨機)和LRU(最近最少使用)。上述分類主要是指Cache在儲存已滿時拋棄已有資料來快取新新增的資料所選取的策略。Cache用來快取資料的資料結構大多數是採用HashMap,以便快速存取。有些Cache的實現還設定有物件的快取時間等,以期達到更細緻的控制。
在後面,提供了一個Cache的簡略實現版本,抽象出一個簡單的Cache介面,一個抽象的AbstractCache類,和上述三種類型Cache的具體實現,其中對於FIFO型別的Cache,提供了Array和List兩種實現方式,相比較而言,List的效率更高。而LRU型別的Cache則是在List型FIFO的Cache基礎之上再行封裝的。時間複雜度上,Cache中快取資料的讀取主要依賴於HashMap的讀取效率;寫入快取資料時,FIFO型別的List實現方式和Random型別為O(1),FIFO型別的Array實現方式為O(n),LRU型別最壞的時間複雜度為O(n)。空間複雜度上,LRU>list方式的FIFO>Array方式的FIFO=Random,不過List形式在記憶體分配上比Array形式更靈活。
Cache介面:
package com.lee.java.learning.cache; /** * An interface defining the basic functions of a cache. * This cache can store the element by the key. * and also it can read the element from the cache by special key. * Except that, It provide methods to Get the size and capacity of * the cache, and judge a element exists in the cache or not. * * @version 1.0 * @author lili06 * */ public interface Cache { /** * Add a element into cache by special key * @param key * @param value */ public void addElement(Object key, Object value); /** * Read the element from the cache by special key * @param key * @return */ public Object getElement(Object key); /** * If a element attached with the special key has been cached, * this method return true, otherwise false. * @param key * @return */ public boolean isExist(Object key); /** * Return the number of elements have been cached in the cache. not to be confused with * the {@link #capacity()} which returns the number * of elements that can be held in the cache at one time. * @return the number of value object cached at one time */ public int size(); /** * Returns the maximum number of elements that can be cached at one time. * <p> * @return The maximum number of elements that can be cached at one time. */ public int capacity(); /** * Clear all the cached elements. */ public void clear(); }
AbstractCache抽象類:
package com.lee.java.learning.cache; import java.io.Serializable; import java.util.HashMap; import java.util.List; public abstract class AbstractCache implements Cache, Serializable { private static final long serialVersionUID = 2046841211971587188L; public static final int DEFAULT_CAPACITY = 20; int size; int capacity; HashMap cacheMap; AbstractCache(int capacity) { size = 0; if(capacity <= 0) { throw new RuntimeException("the cache capacity must be a positive integer..."); } this.capacity = capacity; cacheMap = new HashMap(capacity); } public abstract void addElement(Object key, Object value); public synchronized Object getElement(Object key) { Object obj = null; obj = cacheMap.get(key); if(obj != null) { return ((CacheEntity)obj).value; } return null; } public synchronized void clear() { cacheMap.clear(); size = 0; } public synchronized boolean isExist(Object key) { if(getElement(key) != null) { return true; } return false; } public final synchronized int size() { return size; } public final synchronized int capacity() { return capacity; } public final synchronized boolean isFull() { return size == capacity; } static class CacheEntity { Object key; Object value; CacheEntity() { key = null; value = null; } } }
CacheRandom實現類:
package com.lee.java.learning.cache;
import java.util.Random;
public class CacheRandom extends AbstractCache {
private static final long serialVersionUID = -3906908597773607461L;
private Random random;
private RandomCacheEntity[] cache;
public CacheRandom() {
this(AbstractCache.DEFAULT_CAPACITY);
}
public CacheRandom(int capacity) {
super(capacity);
random = new Random(System.currentTimeMillis());
cache = new RandomCacheEntity[capacity];
for(int i=0; i<capacity; i++) {
cache[i] = new RandomCacheEntity(i);
}
}
@Override
public synchronized void addElement(Object key, Object value) {
int index = 0;
Object obj = null;
obj = cacheMap.get(key);
if(obj != null) {
RandomCacheEntity entity = (RandomCacheEntity)obj;
entity.value = value;
return;
}
if(!isFull()) {
index = size;
size++;
}else {
index = random.nextInt(capacity);
cacheMap.remove(cache[index].key);
}
cache[index].key = key;
cache[index].value = value;
cacheMap.put(key, cache[index]);
}
@Override
public synchronized void clear() {
super.clear();
for(int i=0; i<capacity; i++) {
cache[i] = new RandomCacheEntity(i);
}
}
static class RandomCacheEntity extends CacheEntity {
int index;
RandomCacheEntity(int index) {
this.index = index;
}
}
}
ArrayCacheFIFO實現類:
package com.lee.java.learning.cache;
public class ArrayCacheFIFO extends AbstractCache {
private static final long serialVersionUID = -4299736285228473989L;
private int current = 0;
private ArrayCacheEntity[] cache;
public ArrayCacheFIFO() {
this(AbstractCache.DEFAULT_CAPACITY);
}
public ArrayCacheFIFO(int capacity) {
super(capacity);
cache = new ArrayCacheEntity[capacity];
for(int i=0; i<capacity; i++) {
cache[i] = new ArrayCacheEntity(i);
}
}
@Override
public synchronized void addElement(Object key, Object value) {
int index = current;
Object obj = null;
obj = getElement(key);
if(obj != null) {
ArrayCacheEntity entity = (ArrayCacheEntity)obj;
updateOrder(entity, value);
return;
}
if(!isFull()) {
index = size;
size++;
}else {
index = current;
if(++current >= capacity) {
current = 0;
}
cacheMap.remove(cache[index].key);
}
cache[index].key = key;
cache[index].value = value;
cacheMap.put(key, cache[index]);
}
private void updateOrder(ArrayCacheEntity entity, Object value) {
int index = entity.index;
Object key = entity.key;
if(!isFull()) {
for(int i=index; i<size-1; i++) {
cache[i].key = cache[i+1].key;
cache[i].value = cache[i+1].value;
}
cache[size-1].key = key;
cache[size-1].value = value;
for(int i=index; i<size; i++) {
cacheMap.put(cache[i].key, cache[i]);
}
}else {
if(index == current) {
cache[current].value = value;
cacheMap.put(cache[current].key, cache[current]);
if(++current >= capacity) {
current = 0;
}
}else if(index < current) {
if((current-index)*2 <= capacity) {
for(int i=index; i<current-1; i++) {
cache[i].key = cache[i+1].key;
cache[i].value = cache[i+1].value;
}
cache[current-1].key = key;
cache[current-1].value = value;
for(int i=index; i<current; i++) {
cacheMap.put(cache[i].key, cache[i]);
}
}else {
for(int i=index; i > 0; i--) {
cache[i].key = cache[i-1].key;
cache[i].value = cache[i-1].value;
}
cache[0].key = cache[capacity-1].key;
cache[0].value = cache[capacity-1].value;
for(int i=capacity-1; i>current; i--) {
cache[i].key = cache[i-1].key;
cache[i].value = cache[i-1].value;
}
cache[current].key = key;
cache[current].value = value;
int i = index, count = capacity - (current - index - 1);
while(count-- > 0) {
i = (i+capacity) % capacity;
cacheMap.put(cache[i].key, cache[i]);
i--;
}
current++;
}
}else {
if((index-current+1)*2 <= capacity) {
for(int i=index; i>current; i--) {
cache[i].key = cache[i-1].key;
cache[i].value = cache[i-1].value;
}
cache[current].key = key;
cache[current].value = value;
for(int i=current; i<=index; i++) {
cacheMap.put(cache[i].key, cache[i]);
}
current++;
}else {
for(int i=index; i<capacity-1; i++) {
cache[i].key = cache[i+1].key;
cache[i].value = cache[i+1].value;
}
cache[capacity-1].key = cache[0].key;
cache[capacity-1].value = cache[0].value;
for(int i=0; i<current-1; i++) {
cache[i].key = cache[i+1].key;
cache[i].value = cache[i+1].value;
}
cache[current-1].key = key;
cache[current-1].value = value;
int i = index, count = capacity - (index-current);
while(count-- > 0) {
i %= capacity;
cacheMap.put(cache[i].key, cache[i]);
i++;
}
}
}
}
}
@Override
public synchronized void clear() {
for(int i=0; i<cache.length; i++) {
cache[i] = new ArrayCacheEntity(i);
}
}
static class ArrayCacheEntity extends CacheEntity {
int index;
ArrayCacheEntity(int index) {
this.index = index;
}
}
}
ListCacheFIFO實現類:
package com.lee.java.learning.cache;
public class ListCacheFIFO extends AbstractCache {
private static final long serialVersionUID = -1804440082070314369L;
protected ListCacheEntity head;
public ListCacheFIFO() {
this(AbstractCache.DEFAULT_CAPACITY);
}
public ListCacheFIFO(int capacity) {
super(capacity);
head = new ListCacheEntity();
head.pre = head.next = head;
}
@Override
public synchronized void addElement(Object key, Object value) {
Object obj = null;
obj = cacheMap.get(key);
if(obj != null) {
ListCacheEntity entity = (ListCacheEntity)obj;
entity.value = value;
// update order
entity.pre.next = entity.next;
entity.next.pre = entity.pre;
entity.pre = entity.next = null;
moveToFront(entity, head);
cacheMap.put(key, entity);
return;
}
if(!isFull()) {
size++;
}else {
ListCacheEntity temp = head.next;
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
temp.pre = temp.next = null;
cacheMap.remove(temp.key);
}
ListCacheEntity entity = new ListCacheEntity();
entity.key = key;
entity.value = value;
moveToFront(entity, head);
cacheMap.put(key, entity);
}
protected void moveToFront(ListCacheEntity movedEntity, ListCacheEntity desEntity) {
desEntity.pre.next = movedEntity;
movedEntity.pre = desEntity.pre;
desEntity.pre = movedEntity;
movedEntity.next = desEntity;
}
@Override
public synchronized void clear() {
super.clear();
head.pre = head.next = head;
}
static class ListCacheEntity extends CacheEntity {
ListCacheEntity pre;
ListCacheEntity next;
ListCacheEntity() {
pre = next = null;
}
}
}
CacheLRU實現類:
package com.lee.java.learning.cache;
import com.lee.java.learning.cache.ListCacheFIFO.ListCacheEntity;
public class CacheLRU extends ListCacheFIFO {
private static final long serialVersionUID = 1122993622171221931L;
public CacheLRU() {
this(ListCacheFIFO.DEFAULT_CAPACITY);
}
public CacheLRU(int capacity) {
super(capacity);
head = new CacheLRUEntity();
head.pre = head.next = head;
}
@Override
public synchronized void addElement(Object key, Object value) {
Object obj = null;
obj = cacheMap.get(key);
if(obj != null) {
CacheLRUEntity entity = (CacheLRUEntity)obj;
entity.value = value;
entity.count++;
// update order
CacheLRUEntity it = (CacheLRUEntity) entity.next;
while(it != head && it.count <= entity.count) {
it = (CacheLRUEntity)it.next;
}
if(it == entity.next) {
// do nothing
}else {
entity.pre.next = entity.next;
entity.next.pre = entity.pre;
entity.pre = entity.next = null;
moveToFront(entity, it);
}
cacheMap.put(key, entity);
return;
}
if(!isFull()) {
size++;
}else {
ListCacheEntity temp = head.next;
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
temp.pre = temp.next = null;
cacheMap.remove(temp.key);
}
CacheLRUEntity entity = new CacheLRUEntity();
entity.count = 1;
entity.key = key;
entity.value = value;
CacheLRUEntity it = (CacheLRUEntity) head.next;
while(it != head && it.count <= entity.count) {
it = (CacheLRUEntity)it.next;
}
moveToFront(entity, it);
cacheMap.put(key, entity);
}
@Override
public synchronized Object getElement(Object key) {
Object obj = null;
obj = cacheMap.get(key);
if(obj != null) {
CacheLRUEntity entity = (CacheLRUEntity)obj;
entity.count++;
// update order
CacheLRUEntity it = (CacheLRUEntity) entity.next;
while(it != head && it.count <= entity.count) {
it = (CacheLRUEntity)it.next;
}
if(it == entity.next) {
// do nothing
}else {
entity.pre.next = entity.next;
entity.next.pre = entity.pre;
entity.pre = entity.next = null;
moveToFront(entity, it);
}
return entity.value;
}
return null;
}
static class CacheLRUEntity extends ListCacheEntity {
int count;
CacheLRUEntity() {
count = 0;
}
}
}