/** * <p> * Creates a new cache based on the given {@link Retriever} and with given maximum size. * </p> * * @param retriever * object which can retrieve values for keys * @param maxEntries * maximum number of entries the cache will store before evicting some */ public Cache(Retriever<? super K,? extends V> retriever, int maxEntries) { Preconditions.checkArgument(retriever != null, "retriever is null"); Preconditions.checkArgument(maxEntries >= 1, "maxEntries must be at least 1"); cache = new FastMap<K, V>(11, maxEntries); this.retriever = retriever; }
@Override public void putAll(Map<? extends K,? extends V> map) { for (Entry<? extends K,? extends V> entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } }
growAndRehash(); } else { rehash(); int index = findForAdd(key); if (keys[index] == key) { V oldValue = values[index]; clearStaleEntry(index);
private void growAndRehash() { if (keys.length * loadFactor >= RandomUtils.MAX_INT_SMALLER_TWIN_PRIME) { throw new IllegalStateException("Can't grow any more"); } rehash(RandomUtils.nextTwinPrime((int) (loadFactor * keys.length))); }
@Override public boolean containsKey(Object key) { return key != null && keys[find(key)] != null; }
/** * <p> * Uncaches any existing value for a given key. * </p> * * @param key * cache key */ public void remove(K key) { synchronized (cache) { cache.remove(key); } }
/** * Clears all cache entries whose key matches the given predicate. */ public void removeKeysMatching(MatchPredicate<K> predicate) { synchronized (cache) { Iterator<K> it = cache.keySet().iterator(); while (it.hasNext()) { K key = it.next(); if (predicate.matches(key)) { it.remove(); } } } }
/** * Clears all cache entries whose value matches the given predicate. */ public void removeValueMatching(MatchPredicate<V> predicate) { synchronized (cache) { Iterator<V> it = cache.values().iterator(); while (it.hasNext()) { V value = it.next(); if (predicate.matches(value)) { it.remove(); } } } }
/** * <p> * Returns cached value for a key. If it does not exist, it is loaded using a {@link Retriever}. * </p> * * @param key * cache key * @return value for that key * @throws TasteException * if an exception occurs while retrieving a new cached value */ @Override public V get(K key) throws TasteException { V value; synchronized (cache) { value = cache.get(key); } if (value == null) { return getAndCacheValue(key); } return value == NULL ? null : value; }
@Test public void testKeySet() { FastMap<String, String> map = buildTestFastMap(); Collection<String> expected = Sets.newHashSetWithExpectedSize(3); expected.add("foo"); expected.add("baz"); expected.add("alpha"); Set<String> actual = map.keySet(); assertTrue(expected.containsAll(actual)); assertTrue(actual.containsAll(expected)); Iterator<String> it = actual.iterator(); while (it.hasNext()) { String value = it.next(); if (!"baz".equals(value)) { it.remove(); } } assertTrue(map.containsKey("baz")); assertFalse(map.containsKey("foo")); assertFalse(map.containsKey("alpha")); }
@Test public void testValues() { FastMap<String, String> map = buildTestFastMap(); Collection<String> expected = Sets.newHashSetWithExpectedSize(3); expected.add("bar"); expected.add("bang"); expected.add("beta"); Collection<String> actual = map.values(); assertTrue(expected.containsAll(actual)); assertTrue(actual.containsAll(expected)); Iterator<String> it = actual.iterator(); while (it.hasNext()) { String value = it.next(); if (!"bang".equals(value)) { it.remove(); } } assertTrue(map.containsValue("bang")); assertFalse(map.containsValue("bar")); assertFalse(map.containsValue("beta")); }
/** * <p> * Clears the cache. * </p> */ public void clear() { synchronized (cache) { cache.clear(); } }
private void growAndRehash() { if (keys.length * loadFactor >= RandomUtils.MAX_INT_SMALLER_TWIN_PRIME) { throw new IllegalStateException("Can't grow any more"); } rehash(RandomUtils.nextTwinPrime((int) (loadFactor * keys.length))); }
@Override public boolean containsKey(Object key) { return key != null && keys[find(key)] != null; }
/** * <p> * Uncaches any existing value for a given key. * </p> * * @param key * cache key */ public void remove(K key) { synchronized (cache) { cache.remove(key); } }
/** * Clears all cache entries whose key matches the given predicate. */ public void removeKeysMatching(MatchPredicate<K> predicate) { synchronized (cache) { Iterator<K> it = cache.keySet().iterator(); while (it.hasNext()) { K key = it.next(); if (predicate.matches(key)) { it.remove(); } } } }
/** * Clears all cache entries whose value matches the given predicate. */ public void removeValueMatching(MatchPredicate<V> predicate) { synchronized (cache) { Iterator<V> it = cache.values().iterator(); while (it.hasNext()) { V value = it.next(); if (predicate.matches(value)) { it.remove(); } } } }
/** * <p> * Returns cached value for a key. If it does not exist, it is loaded using a {@link Retriever}. * </p> * * @param key * cache key * @return value for that key * @throws TasteException * if an exception occurs while retrieving a new cached value */ @Override public V get(K key) throws TasteException { V value; synchronized (cache) { value = cache.get(key); } if (value == null) { return getAndCacheValue(key); } return value == NULL ? null : value; }