ExpiringCache { public V get(final K key) { Entry<V> entry = computeIfAbsent(key); return entry.getValue(); } ExpiringCache(final long expireAfterMs, final Function<K, V> getter);  ExpiringCache(final long expireAfterMs, final Function<K, V> getter,
                       final long expirationFreqMs, final Clock clock); V get(final K key); List<V> values(); int size(); @Override String toString();  }
@Test public void testGet() throws Exception { ManualClock clock = new ManualClock(0L); CountingFun fun = new CountingFun(); ExpiringCache<String, Integer> map = new ExpiringCache<>(100L, fun, 100L, clock); Integer three = map.get("foo"); assertEquals(three, Integer.valueOf(3)); Integer threeAgain = map.get("foo"); assertEquals(threeAgain, Integer.valueOf(3)); assertEquals(fun.numCalled, 1, "Properly caches computations"); clock.set(200L); Thread.sleep(200L); Integer threeOnceMore = map.get("foo"); assertEquals(threeOnceMore, Integer.valueOf(3)); assertEquals(fun.numCalled, 2, "Properly expires unused entries"); }