-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathWeakValueHashMap.java
More file actions
168 lines (142 loc) · 4.32 KB
/
WeakValueHashMap.java
File metadata and controls
168 lines (142 loc) · 4.32 KB
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package com.epublica.java;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
// inspired by http://www.java2s.com/Code/Java/Collections-Data-Structure/WeakValueHashMap.htm
// own implementation
/**
* The desired behaviour of an in-memory cache is to keep a weak reference to the cached object,
* this will allow the garbage collector to remove an object from memory once it isn't needed
* anymore.
*
* A {@link HashMap} doesn't help here since it will keep hard references for key and
* value objects. A {@link WeakHashMap} doesn't either, because it keeps weak references to the
* key objects, but we want to track the value objects.
*
* This implementation of a Map uses a {@link WeakReference} to the value objects. Once the
* garbage collector decides it wants to finalize a value object, it will be removed from the
* map automatically.
*
* @param <K> - the type of the key object
* @param <V> - the type of the value object
*/
public class WeakValueHashMap<K,V> extends AbstractMap<K,V> {
// the internal hash map to the weak references of the actual value objects
private HashMap<K, WeakValue<V>> references;
// the garbage collector's removal queue
private ReferenceQueue<V> gcQueue;
/**
* Creates a WeakValueHashMap with a desired initial capacity
* @param capacity - the initial capacity
*/
public WeakValueHashMap(int capacity) {
references = new HashMap<K, WeakValue<V>>(capacity);
gcQueue = new ReferenceQueue<V>();
}
/**
* Creates a WeakValueHashMap with an initial capacity of 1
*/
public WeakValueHashMap() {
this(1);
}
/**
* Creates a WeakValueHashMap and copies the content from an existing map
* @param map - the map to copy from
*/
public WeakValueHashMap(Map<? extends K, ? extends V> map) {
this(map.size());
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet() ) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public V put(K key, V value) {
processQueue();
WeakValue<V> valueRef = new WeakValue<V>(key, value, gcQueue);
return getReferenceValue(references.put(key, valueRef));
};
@Override
public V get(Object key) {
processQueue();
return getReferenceValue(references.get(key));
}
@Override
public V remove(Object key) {
return getReferenceValue(references.get(key));
}
@Override
public void clear() {
references.clear();
}
@Override
public boolean containsKey(Object key) {
processQueue();
return references.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
processQueue();
for (Map.Entry<K, WeakValue<V>> entry : references.entrySet()) {
if (value == getReferenceValue(entry.getValue())) {
return true;
}
}
return false;
}
@Override
public Set<K> keySet() {
processQueue();
return references.keySet();
}
@Override
public int size() {
processQueue();
return references.size();
}
@Override
public Set<Map.Entry<K,V>> entrySet() {
processQueue();
Set<Map.Entry<K,V>> entries = new LinkedHashSet<Map.Entry<K,V>>();
for (Map.Entry<K,WeakValue<V>> entry : references.entrySet()) {
entries.add(new AbstractMap.SimpleEntry<K,V>(entry.getKey(), getReferenceValue(entry.getValue())));
}
return entries;
}
public Collection<V> values() {
processQueue();
Collection<V> values = new ArrayList<V>();
for (WeakValue<V> valueRef : references.values()) {
values.add(getReferenceValue(valueRef));
}
return values;
}
private V getReferenceValue(WeakValue<V> valueRef) {
return valueRef == null ? null : valueRef.get();
}
// remove entries once their value is scheduled for removal by the garbage collector
@SuppressWarnings("unchecked")
private void processQueue() {
WeakValue<V> valueRef;
while ( (valueRef = (WeakValue<V>) gcQueue.poll()) != null ) {
references.remove(valueRef.getKey());
}
}
// for faster removal in {@link #processQueue()} we need to keep track of the key for a value
private class WeakValue<T> extends WeakReference<T> {
private final K key;
private WeakValue(K key, T value, ReferenceQueue<T> queue) {
super(value, queue);
this.key = key;
}
private K getKey() {
return key;
}
}
}