A Java-based library that implements multiple cache eviction strategies, including LRU, LFU, MRU, MFU, FIFO, and LIFO. Perfect for experimenting with in-memory caching behavior using fixed or variable-capacity caches. However, since most implementations internally use an array, they are not fit for storing large quantities of data; if that is your case, I would suggest you to use a library like Guava or other any heap-based caches.
Be advised that this library is in no way ready for production as it is not fully covered by integration and unit tests just yet, so use it at your own discretion.
if you would like to know more about cache eviction policies and which one best fits your case, you can start by reading this article.
com.asterexcrisys.evicache
β
βββ maps # All cache maps
β βββ access # Access-based cache implementations
β β βββ fixed # Fixed-size versions
β β β βββ LRUCache.java
β β β βββ MRUCache.java
β β β
β β βββ variable # Variable-size versions
β β βββ LRUCache.java
β β βββ MRUCache.java
β β
β βββ frequency # Frequency-based cache implementations
β β βββ fixed # Fixed-size versions
β β β βββ LFUCache.java
β β β βββ MFUCache.java
β β β
β β βββ variable # Variable-size versions
β β βββ LFUCache.java
β β βββ MFUCache.java
β β
β βββ order # Order-based cache implementations
β β βββ fixed # Fixed-size versions
β β β βββ FIFOCache.java
β β β βββ LIFOCache.java
β β β
β β βββ variable # Variable-size versions
β β βββ FIFOCache.java
β β βββ LIFOCache.java
β β
β βββ time # Time-based cache implementations
β β βββ fixed # Fixed-size versions
β β β βββ TimeCache.java
β β β βββ ExpireCache.java
β β β
β β βββ variable # Variable-size versions
β β βββ TimeCache.java
β β βββ ExpireCache.java
β β
β βββ extra # Extra cache implementations (e.g priority-based or random-based)
β βββ fixed # Fixed-size versions
β β βββ PriorityCache.java
β β βββ RandomCache.java
β β
β βββ variable # Variable-size versions
β βββ PriorityCache.java
β βββ RandomCache.java
β
βββ entries # All cache entries
β βββ BasicCacheEntry.java # Cache entry used by most implementations (has only the basic 'key' and 'value' fields)
β βββ PriorityCacheEntry.java # Cache entry used only by PriorityCache (has one additional 'priority' field)
β βββ ExpireCacheEntry.java # Cache entry used only by ExpireCache (has two additional 'time' and 'unit' fields)
β
βββ models # All cache-related models
β βββ EvictionPolicy.java # Enumeration that contains any and all policies of eviction
β βββ ExpireMode.java # Enumeration that contains any and all modes of expire (only used by TimeCache and ExpireCache)
β βββ MetricType.java # Enumeration that contains any and all types of metrics recorded by CacheRecorder
β
βββ exceptions # All cache-related exceptions
β βββ IllegalCacheStateException.java
β βββ InvalidCacheKeyException.java
β βββ CacheUnderflowException.java
β
βββ Cache.java # Interface that any and all caches implement
βββ CacheEntry.java # Interface that any and all cache entries implement
βββ CacheBuilder.java # Self-explanatory, used to easily build caches with different eviction strategies
βββ CacheRecorder.java # Self-explanatory, used to record core metrics of any type of cache
Category | Tool / Technology | Notes |
---|---|---|
Language | Java 17 | Modern LTS version |
Build Tool | Maven | With annotation processing + JMH support |
Testing | JUnit 5 | For unit and integration testing |
Benchmarking | JMH | For precise micro-benchmarking |
Documentation | Javadoc and GitHub Wikis | Interface and API documentation |
Caching Design | Custom Cache<K, V> interface |
Supports top/bottom access and eviction logic |
IDE | IntelliJ IDEA | With annotation processing enabled |
- Java 17 or higher
- Maven (recommended as the project build system)
- IntelliJ IDEA (recommended for project structure)
import com.asterexcrisys.evicache.entries.BasicCacheEntry;
import com.asterexcrisys.evicache.CacheBuilder;
import com.asterexcrisys.evicache.models.ExpireMode;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
Cache<Integer, String> cache = CacheBuilder
.<Integer, String>newBuilder() // Creates a new builder, Cache<Integer, String> will be its return type in this case
.evictionPolicy(EvictionPolicy.LRU) // Sets the policy to use when evicting elements from the cache, only applicable to fixed-length caches
.expireTime(1, TimeUnit.MINUTES) // Sets the expiry time of all elements to 1 minute, only applicable to time-based caches
.expireMode(ExpireMode.AFTER_ACCESS) // Sets the expiry mode to 'after access', meaning the timer of an element will reset after every valid access to it, only applicable to time-based caches
.initialCapacity(10) // Sets the initial and, in this case, total capacity to 10
.capacityFixed(true) // Tells the builder that the returned cache should be of the fixed-length version
.metricsEnabled(true) // Tells the cache to register metrics such as hits and misses (and many others), which can be retrieved through the metrics() method
.build(); // Initializes the cache with the specified parameters
cache.put(new BasicCacheEntry<>(1, "one"));
cache.put(new BasicCacheEntry<>(2, "two"));
cache.put(new BasicCacheEntry<>(3, "three"));
cache.put(new BasicCacheEntry<>(4, "four"));
cache.put(new BasicCacheEntry<>(5, "five"));
cache.get(1);
cache.get(1);
cache.get(2);
cache.get(3);
cache.get(2);
cache.put(new BasicCacheEntry<>(6, "six"));
cache.put(new BasicCacheEntry<>(7, "seven"));
cache.put(new BasicCacheEntry<>(8, "eight"));
cache.put(new BasicCacheEntry<>(9, "nine"));
cache.put(new BasicCacheEntry<>(10, "ten"));
cache.get(3);
cache.get(8);
cache.get(8);
cache.get(7);
cache.put(new BasicCacheEntry<>(0, "zero")); // Evicts the least recently used element (4)
cache.get(0);
System.out.println(cache); // Displays current state of cache through the overridden toString() method
System.out.println(cache.metrics()); // Displays all cache metrics registered up until now (only possible because I enabled the metrics feature in the builder)
}
}
Strategy | Description |
---|---|
LRU | Least Recently Used - removes the least recently accessed item |
LFU | Least Frequently Used - removes the least accessed item |
MRU | Most Recently Used - removes the most recently accessed item |
MFU | Most Frequently Used - removes the most accessed item |
FIFO | First In, First Out - removes the first inserted item |
LIFO | Last In, First Out - removes the last inserted item |
Time | Time - lets you define a global expiry time (applies to all), after which elements will become inaccesible and removed lazily - removes the first inserted item |
Expire | Expire - lets you define an expiry time for each element, after which only that element will become inaccesible and removed lazily - removes the item that is first to expire |
Priority | Priority - removes the item with the least priority value |
Random | Random - removes an item at a random index/position |
Metric | Description |
---|---|
Hit | A hit is any access operation that results in a success (key found) |
Miss | A miss is any access operation that results in a failure (key not found or expired) |
Puts | A put is an operation where an element is either added to the cache or updated |
Removes | A remove is an operation where an element is removed from the cache |
Evictions | An eviction is an automatic operation that happens whenever trying to add an element to a cache that is full |
Clears | A clear is an operation where all entries are removed from the cache |
Size | Self-explanatory, returns the current size (or number of entries) of the cache |
Capacity | Self-explanatory, returns the current capacity (or total occupied space) of the cache |
- β Provide extensive documentation (both via Javadoc and GitHub Wikis)
- β³ Add thread-safe versions
- β³ Add serialization support
- β³ Add iterator support
- β³ Add benchmark performance for each strategy
- β³ Add variable-capacity versions of all current cache implementations
- β³ Add metrics support
Considering the results of the benchmarks performed on access-based and frequency-based caches, reporting that the remove
operation is by far the most time-consuming, I have come to the conclusion that such is because of it immediately nullifying the elements removed and thus indirectly calling the garbage collector to free up the unreferenced memory addresses, for this reason I am taking into account the possibility of implementing a lazy remove
instead.
A lazy remove
will leave the elements referenced in the array and just reduce the size, so that they become 'virtually inaccesible'. The actual removal will happen when they are overwritten by new elements or evicted completely from the cache.
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for more details.
Developed by AsterExcrisys β feel free to contribute, fork, or suggest features!