Skip to content
This repository has been archived by the owner on Jul 10, 2024. It is now read-only.

Commit

Permalink
Optimised the Cache and CacheMap
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshiCodes committed Jun 11, 2023
1 parent 0591556 commit 792e7b5
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 36 deletions.
66 changes: 31 additions & 35 deletions src/main/java/de/joshicodes/rja/cache/Cache.java
Original file line number Diff line number Diff line change
@@ -1,61 +1,46 @@
package de.joshicodes.rja.cache;

import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class Cache<T> {

public static final long DEFAULT_LIFESPAN = TimeUnit.HOURS.toMillis(1);
public static final int DEFAULT_MAX_SIZE = 1000;
public static final long DEFAULT_CLEAR_INTERVAL = TimeUnit.MINUTES.toMillis(1); // 1 Minute

private final int maxSize;
private final HashMap<T, Long> list;

private Timer timer;
private long lastClear = System.currentTimeMillis();

public Cache() {
list = new HashMap<>();
}

/**
* Schedules a task to clear expired entries
* @param delay Delay between each run
* @param unit Unit of the delay
*/
public void scheduleClearExpired(long delay, TimeUnit unit) {
if(timer != null) timer.cancel();
timer = new Timer();
timer.scheduleAtFixedRate(
new TimerTask() {
@Override
public void run() {
clearExpired();
}
},
unit.toMillis(delay),
unit.toMillis(delay)
);
this(DEFAULT_MAX_SIZE);
}

/**
* Cancels the task to clear expired entries
* If no task is scheduled, nothing happens
* @see #scheduleClearExpired(long, TimeUnit)
*/
public void cancelClearExpired() {
if(timer != null) timer.cancel();
public Cache(final int maxSize) {
this.maxSize = maxSize;
list = new HashMap<>();
}

/**
* Clears all expired entries one time
*/
public void clearExpired() {
if ((System.currentTimeMillis() - lastClear) < DEFAULT_CLEAR_INTERVAL) return;
list.keySet().stream().filter(t -> list.get(t) < System.currentTimeMillis()).forEach(list::remove);
lastClear = System.currentTimeMillis();
}

public void add(T t, long lifespan) {
if(list.size() >= maxSize) {
clearExpired(); // Clear expired entries
if(list.size() >= maxSize) {
// If the cache is still full, remove the first entry
list.remove(list.keySet().stream().findFirst().orElse(null));
}
}
list.put(t, System.currentTimeMillis() + lifespan);
}

Expand All @@ -78,7 +63,7 @@ public T get(T t) {
if(list.get(t) >= System.currentTimeMillis()) {
return t;
} else {
list.remove(t);
clearExpired(); // Clear expired entries
}
}
return null;
Expand All @@ -93,9 +78,11 @@ public void clear() {

/**
* Returns a stream of all objects in the cache
* @return
* Also clears all expired entries
* @return Stream of all objects in the cache
*/
public Stream<T> stream() {
clearExpired();
return list.keySet().stream();
}

Expand All @@ -105,14 +92,15 @@ public Stream<T> stream() {
* @return Object if it is in the cache and not expired, null otherwise
*/
public T getIf(Predicate<T> predicate) {
clearExpired();
return list.keySet().stream().filter(predicate).findFirst().orElse(null);
}

public boolean contains(T t) {
if(list.containsKey(t)) {
if(list.get(t) >= System.currentTimeMillis()) {
return true;
} else list.remove(t); // Remove if expired
} else clearExpired();
}
return false;
}
Expand All @@ -121,6 +109,14 @@ public boolean containsIf(Predicate<T> predicate) {
return list.keySet().stream().anyMatch(predicate);
}

/**
* Returns the maximum size of the cache
* @return Maximum size of the cache
*/
public int getMaxSize() {
return maxSize;
}

/**
* Returns the HashMap of all objects and their expiration time
* To just get the objects, use {@link #stream()}
Expand Down
37 changes: 36 additions & 1 deletion src/main/java/de/joshicodes/rja/cache/CacheMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,51 @@

public class CacheMap<K, T> {

private final int maxSize;
private final TrippleMap<K, T, Long> map;

private long lastClear = System.currentTimeMillis();

public CacheMap() {
this(Cache.DEFAULT_MAX_SIZE);
}

public CacheMap(final int maxSize) {
this.maxSize = maxSize;
map = new TrippleMap<>();
}

/**
* Clears all expired entries one time
*/
public void clearExpired() {
if((System.currentTimeMillis() - lastClear) < Cache.DEFAULT_CLEAR_INTERVAL) return;
map.keySet().stream().filter(t -> map.get(t).getSecond() < System.currentTimeMillis()).forEach(map::remove);
lastClear = System.currentTimeMillis();
}


public void put(K key, T t) {
put(key, t, Cache.DEFAULT_LIFESPAN);
}

public void put(K key, T t, long lifespan) {
if(map.size() >= maxSize) {
// Clear expired entries
clearExpired();
// If the cache is still full, remove the first entry
if(map.size() >= maxSize) {
map.remove(map.keySet().iterator().next());
}
}
map.put(key, t, System.currentTimeMillis() + lifespan);
}

public T get(K key) {
Pair<T, Long> pair = map.get(key);
if(pair == null) return null;
if(pair.getSecond() < System.currentTimeMillis()) {
map.remove(key);
clearExpired();
return null;
}
return pair.getFirst();
Expand All @@ -41,22 +67,27 @@ public boolean containsKey(K key) {
}

public boolean containsValue(T t) {
clearExpired();
return getSimpleMap().containsValue(t);
}

public boolean containsFirst(K key, T t) {
clearExpired();
return map.containsFirst(key, t);
}

public boolean containsSecond(K key, Long lifespan) {
clearExpired();
return map.containsSecond(key, lifespan);
}

public boolean containsIf(Predicate<K> predicate) {
clearExpired();
return map.getMap().keySet().stream().anyMatch(predicate);
}

public T getIf(Predicate<K> predicate) {
clearExpired();
return map.getMap().entrySet().stream().filter(entry -> predicate.test(entry.getKey())).findFirst().map(entry -> entry.getValue().getFirst()).orElse(null);
}

Expand All @@ -70,6 +101,10 @@ public HashMap<K, T> getSimpleMap() {
return simpleMap;
}

public int getMaxSize() {
return maxSize;
}

public TrippleMap<K, T, Long> getMap() {
return map;
}
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/de/joshicodes/rja/util/TrippleMap.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.joshicodes.rja.util;

import java.util.HashMap;
import java.util.Set;

/**
* If you just need a simple Map use {@link java.util.HashMap} or {@link java.util.TreeMap}.
Expand Down Expand Up @@ -60,6 +61,14 @@ public void clear() {
map.clear();
}

public int size() {
return map.size();
}

public Set<K> keySet() {
return map.keySet();
}

public Pair<B, C> get(K key) {
return map.get(key);
}
Expand Down

0 comments on commit 792e7b5

Please sign in to comment.