Reworked cache, this time with less hashmap accesses.
This commit is contained in:
parent
dcec9fd7e7
commit
f20515fd45
@ -1,59 +1,38 @@
|
|||||||
package ca.recrown.islandsurvivalcraft.caching;
|
package ca.recrown.islandsurvivalcraft.caching;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
public class Cache<Key, Value> {
|
public class Cache<Key, Value> {
|
||||||
private final int maxSize;
|
private final int maxSize;
|
||||||
private final HashMap<Key, Value> data;
|
private final ConcurrentHashMap<Key, CacheValue<Value>> data;
|
||||||
private final HashMap<Key, Integer> occurrences;
|
private final ConcurrentLinkedQueue<Key> occurrenceOrder;
|
||||||
private final LinkedList<Key> occurrenceOrder;
|
|
||||||
|
|
||||||
public Cache(int maxSize) {
|
public Cache(int maxSize) {
|
||||||
data = new HashMap<>(maxSize);
|
data = new ConcurrentHashMap<>(maxSize);
|
||||||
occurrenceOrder = new LinkedList<>();
|
occurrenceOrder = new ConcurrentLinkedQueue<>();
|
||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
this.occurrences = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cache() {
|
public Cache() {
|
||||||
this(1024);
|
this(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setValue(Key key, Value value) {
|
public void setValue(Key key, Value value) {
|
||||||
if (!data.containsKey(key)) {
|
data.put(key, new CacheValue<>(value));
|
||||||
if (data.size() >= maxSize) {
|
|
||||||
while (occurrences.get(occurrenceOrder.peek()) > 1) {
|
|
||||||
occurencesTrackedPoll();
|
|
||||||
}
|
|
||||||
data.remove(occurencesTrackedPoll());
|
|
||||||
}
|
|
||||||
occurrencesTrackedAdd(key);
|
|
||||||
}
|
|
||||||
data.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void occurrencesTrackedAdd(Key key) {
|
|
||||||
int occ = 0;
|
|
||||||
if (occurrences.get(key) != null) {
|
|
||||||
occ = occurrences.get(key);
|
|
||||||
}
|
|
||||||
occ++;
|
|
||||||
occurrences.put(key, occ);
|
|
||||||
occurrenceOrder.add(key);
|
occurrenceOrder.add(key);
|
||||||
}
|
if (data.size() > maxSize) {
|
||||||
|
|
||||||
private Key occurencesTrackedPoll() {
|
|
||||||
Key key = occurrenceOrder.poll();
|
|
||||||
int occ = 0;
|
int occ = 0;
|
||||||
if (occurrences.get(key) != null) {
|
do {
|
||||||
occ = occurrences.get(key);
|
Key potentialKey = occurrenceOrder.poll();
|
||||||
|
CacheValue<Value> potential = data.get(potentialKey);
|
||||||
|
potential.decreaseOccurence();
|
||||||
|
occ = potential.getOccurrence();
|
||||||
|
if (occ < 1) {
|
||||||
|
data.remove(potentialKey);
|
||||||
|
}
|
||||||
|
} while (occ > 0);
|
||||||
}
|
}
|
||||||
occ--;
|
|
||||||
if (occ < 0) throw new IllegalStateException();
|
|
||||||
occurrences.put(key, occ);
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,14 +42,17 @@ public class Cache<Key, Value> {
|
|||||||
* @param key the key associated with the value.
|
* @param key the key associated with the value.
|
||||||
* @return the value associated to the key.
|
* @return the value associated to the key.
|
||||||
*/
|
*/
|
||||||
public synchronized Value getValue(Key key) {
|
public Value getValue(Key key) {
|
||||||
occurrencesTrackedAdd(key);
|
CacheValue<Value> res = data.get(key);
|
||||||
return data.get(key);
|
if (res == null) return null;
|
||||||
|
|
||||||
|
res.increaseOccurrence();
|
||||||
|
occurrenceOrder.add(key);
|
||||||
|
return res.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void clearCache() {
|
public void clearCache() {
|
||||||
data.clear();
|
data.clear();
|
||||||
occurrenceOrder.clear();
|
occurrenceOrder.clear();
|
||||||
occurrences.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package ca.recrown.islandsurvivalcraft.caching;
|
||||||
|
|
||||||
|
public class CacheValue <ValueType> {
|
||||||
|
private int occurrence = 1;
|
||||||
|
private final ValueType value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the occurrence
|
||||||
|
*/
|
||||||
|
public int getOccurrence() {
|
||||||
|
return occurrence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseOccurrence() {
|
||||||
|
occurrence++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decreaseOccurence() {
|
||||||
|
occurrence--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheValue(ValueType value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value
|
||||||
|
*/
|
||||||
|
public ValueType getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -66,10 +66,10 @@ public class CacheTest {
|
|||||||
|
|
||||||
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
||||||
|
|
||||||
int[] expected = new int[amount];
|
int[] values = new int[amount];
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
expected[i] = random.nextInt();
|
values[i] = random.nextInt();
|
||||||
largeCache.setValue(i, expected[i]);
|
largeCache.setValue(i, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < amount /2; i++) {
|
for (int i = 0; i < amount /2; i++) {
|
||||||
@ -77,7 +77,7 @@ public class CacheTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int i = amount /2; i < amount; i++) {
|
for (int i = amount /2; i < amount; i++) {
|
||||||
assertEquals(expected[i], largeCache.getValue(i), "Current accessor: " + i);
|
assertEquals(values[i], largeCache.getValue(i), "Current accessor: " + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,10 +88,10 @@ public class CacheTest {
|
|||||||
|
|
||||||
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
||||||
|
|
||||||
int[] expected = new int[amount];
|
int[] values = new int[amount];
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
expected[i] = random.nextInt();
|
values[i] = random.nextInt();
|
||||||
largeCache.setValue(i, expected[i]);
|
largeCache.setValue(i, values[i]);
|
||||||
largeCache.getValue(0);
|
largeCache.getValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,9 +100,9 @@ public class CacheTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int i = (amount /2) + 1; i < amount; i++) {
|
for (int i = (amount /2) + 1; i < amount; i++) {
|
||||||
assertEquals(expected[i], largeCache.getValue(i), "Current accessor: " + i);
|
assertEquals(values[i], largeCache.getValue(i), "Current accessor: " + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(expected[0], largeCache.getValue(0));
|
assertEquals(values[0], largeCache.getValue(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user