eviction-frequency
Least-frequently-used cache eviction
LeastFrequentlyUsed is an EvictionStrategy that, when an Insert exceeds its capacity, removes the key used least often, and among equals the one used longest ago.
Rationale
Why. Some keys are asked for all day, and a recency order forgets one after a single quiet spell.
Key Concepts
Frequency
Role. Each Insert and Hit adds one to its key's count. A Miss changes nothing, and an Evict discards the count.
Pattern. STRATEGY, OBSERVER
How It Works
Guarantees. No public parameter takes null and no public method returns it, declared with contrax's @PreCondition(IsNotNull.class) and @PostCondition(IsNotNull.class).
Thread safety. An instance is not thread-safe: events must reach update one at a time.
Lane
Purpose. Eviction for workloads with a stable set of popular keys.
Alternative. eviction-recency when popularity shifts.
When to avoid. Popularity that shifts over time: a once-popular key outlasts a newly popular one.
Usage
Room for two: a is read three times and stays, while b and then c leave
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
import software.visionary.cache.Cache;
import software.visionary.cache.capacity.fixed.FixedSize;
import software.visionary.cache.events.CacheEvent;
import software.visionary.cache.events.Evict;
import software.visionary.cache.eviction.frequency.LeastFrequentlyUsed;
import software.visionary.cache.policy.CachePolicy;
import software.visionary.cache.topology.read.through.ReadThroughCacheFactory;
import software.visionary.events.Observer;
Map<String, Integer> source = Map.of("a", 1, "b", 2, "c", 3, "d", 4);
Supplier<Cache<String, Integer>> caches = new ReadThroughCacheFactory<>(
key -> Optional.ofNullable(source.get(key)),
new CachePolicy<>(() -> new FixedSize<>(2), LeastFrequentlyUsed::new, Instant::now));
Observer<CacheEvent<String, ?>> printEvictions = event -> {
if (event instanceof Evict<String, ?> evicted) { System.out.println("evicted " + evicted.key()); }
};
var cache = caches.get();
cache.add(printEvictions);
Stream.of("a", "a", "a", "b", "c", "d").forEach(cache::apply);
Output
evicted b
evicted ca and b are each used twice and b's last use is older, so b leaves
var tied = caches.get();
tied.add(printEvictions);
Stream.of("a", "b", "b", "a", "c").forEach(tied::apply);
Output
evicted bDependencies
software.visionary:cache:2026.10.13at Compilesoftware.visionary.contrax:processor:2026.10.13at Processorsoftware.visionary:junit-closure:2026.09.17at Testing
Views
Drawn from the release. A link in a view opens its source at the release tag.