使用ArrayDeque实现缓存

时间:2012-11-05 14:41:47

标签: java generics

简介

我使用ArrayDeque并使用Generics解决方案实施了一个带有LRU策略的简单缓存:

public class Cache<T> extends ArrayDeque<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    private int MAX_SIZE;

    public Cache(int maxSize) {
        MAX_SIZE = maxSize;
    }

    public void store(T e) {
        if (super.size() >= MAX_SIZE) {                 
            this.pollLast();
        }
        this.addFirst(e);       
    }

    public T fetch(T e) {
        Iterator<T> it = this.iterator();
        while (it.hasNext()) {
            T current = it.next();
            if (current.equals(e)) {
                this.remove(current);
                this.addFirst(current);
                return current;
            }
        }
        return null;
    }

}

问题

当我实例化类并推送一个元素时,

Cache<CachedClass> cache = new Cache<CachedClass>(10);
cache.store(new CachedClass());

此时队列中不包含任何内容。

为什么会这样?


观察

顺便说一下,CachedClass会覆盖方法.equals()


测试

 public class CacheTest {

    @Test
    public void testStore() {
        Cache<Integer> cache = new Cache<Integer>(3);

        cache.store(1);
        assertTrue(cache.contains(1));

        cache.store(2);
        cache.store(3);
        cache.store(4);

        assertEquals(cache.size(), 3);      
    }

    @Test
    public void testFetch() {
        Cache<Context> cache = new Cache<Context>(2);

        Context c1 = new Context(1);
        Context c2 = new Context(2);

        cache.store(c1);
        cache.store(c2);                

        assertEquals((Context) cache.peekFirst(), (new Context(2)));

        Context c = cache.fetch(c1);

        assertTrue(c == c1);        
        assertEquals(cache.size(), 2);
        assertEquals((Context) cache.peekFirst(), (new Context(1)));

    }

 }

编辑它成功通过了两项测试。

  

它通过了第一次测试。它无法在

上抛出AssertException
assertTrue(cache.peekFirst() == 1);
     

第二次测试,

3 个答案:

答案 0 :(得分:1)

LinkedHashMap的Javadoc说

  

“这种地图非常适合构建LRU缓存。”

你真的需要有理由忽视这一点。我的猜测是,在使用Map的实现中,性能将无法区分,并且在使用Map时获得的性能要好得多 - 但是,嘿,为什么不运行自己的基准测试。

最后,您的实现(以及LinkedHashMap提供的实现)不是线程安全的。如果这对您来说是个问题,则同步逻辑将增加性能开销。

答案 1 :(得分:0)

我在主要方法中这样做:

    Cache<Integer> cache = new Cache<Integer>(10);
    cache.store(new Integer(0)); 
    System.out.println(cache.size()); // prints 1
    Integer foo = cache.fetch(new Integer(0));
    System.out.println(foo == null); // prints false

并打印1,因此Cache中有1个元素。工作正常。

用于获取CachedClass muss覆盖equals()。您的代码无需更改就可以完美地运行。

答案 2 :(得分:0)

我使用LinkedHashMap作为LRU缓存。

public static <K,V> Map<K,V> lruCache(final int maxSize) {
    return new LinkedHashMap<K,V>(maxSize*4/3, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
            return size() > maxSize;
        }
    };
}

一个重要的区别是密钥与值相关联。如果你将一个缓存作为一个集来实现,你可以做的就是确认你已经拥有的对象是否在缓存中,这对我来说并不那么有用。

测试

@Test
public void testStore() {
    Map<Integer, String> cache = lruCache(3);
    cache.put(1, "one");
    assertEquals("one", cache.get(1));

    cache.put(2, "two");
    cache.put(3, "three");
    cache.put(4, "four");

    assertEquals(cache.size(), 3);
    assertEquals(null, cache.get(1));
}

@Test
public void testFetch() {
    Map<Integer, String> cache = lruCache(3);

    cache.put(1, "one");
    cache.put(2, "two");

    assertEquals((Integer) 1, cache.keySet().iterator().next());

    String s = cache.get(1);

    assertEquals("one", s);
    assertEquals(cache.size(), 2);
    assertEquals((Integer) 2, cache.keySet().iterator().next());
}