summaryrefslogtreecommitdiff
path: root/source/com/c2kernel/utils/SoftCache.java
blob: 6680f3068fca685c44c57052b9095ece9f8ef633 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.c2kernel.utils;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.*;

/*******************************************************************************
 * SoftReferences are reaped if no strong references are left and the vm is
 * running out of memory. Most caches in the kernel use this.
 * 
 * $Revision: 1.5 $ $Date: 2004/10/29 13:29:09 $
 ******************************************************************************/
public class SoftCache<K, V> extends AbstractMap<K, V> {

    private final Map<K, SoftValue<V>> hash = new HashMap<K, SoftValue<V>>();
    private final int minSize;
    private final LinkedList<V> hardCache = new LinkedList<V>();
    private final ReferenceQueue<V> queue = new ReferenceQueue<V>();

    public SoftCache() {
        this(0);
    }

    public SoftCache(int minSize) {
        this.minSize = minSize;
    }

    @Override
    public V get(Object key) {
        V result = null;
        SoftValue<V> soft_ref = hash.get(key);
        if (soft_ref != null) {
            result = soft_ref.get();
            if (result == null)
                hash.remove(key);
            else
                if (minSize > 0) { // add to hard cache so it's not reaped for a while
                    hardCache.addFirst(result);
                    if (hardCache.size() > minSize) // trim last one off
                        hardCache.removeLast();
                }
        }
        return result;
    }

    public V put(K key, V value) {
        processQueue();
        if (minSize > 0) {
            hardCache.addFirst(value);
            if (hardCache.size() > minSize)
                hardCache.removeLast();
        }
        hash.put(key, new SoftValue<V>(key, value, queue));
        return value;
    }

    public V remove(Object key) {
        processQueue();
        return hash.remove(key).get();
    }

    public void clear() {
        hardCache.clear();
        while(queue.poll()!=null);
        hash.clear();
    }

    public int size() {
        processQueue();
        return hash.size();
    }
    
    public Set<K> keySet() {
        processQueue();
        return hash.keySet();
    }

    public Set<Map.Entry<K, V>> entrySet() {
        // Would have to create another Map to do this - too expensive
        // Throwing runtime expensive is dangerous, but better than nulls
        throw new UnsupportedOperationException();
    }    
    
    private static class SoftValue<V> extends SoftReference<V> {
        private final Object key;
        private SoftValue(Object key, V value, ReferenceQueue<V> q) {
            super(value, q);
            this.key = key;
        }
    }
    
    /**
     * Look for values that have been reaped, and remove their keys from the cache
     */
    private void processQueue() {
        SoftValue sv;
        while ((sv = (SoftValue) queue.poll()) != null) {
            hash.remove(sv.key);
        }
    }
    
}