Skip to content

Instantly share code, notes, and snippets.

@lamvak
Last active August 14, 2017 19:11
Show Gist options
  • Save lamvak/24cc57ca37ebf3facd324cebe0d5ffcc to your computer and use it in GitHub Desktop.
Save lamvak/24cc57ca37ebf3facd324cebe0d5ffcc to your computer and use it in GitHub Desktop.
package pl.lamvak.utils;
public class CompositingRepository<K, L, V> implements Repository<K, V> {
private Repository<K, L> first;
private Repository<L, V> second;
public CompositingRepository(Repository<K, L> first, Repository<L, V> second) {
this.first = first;
this.second = second;
}
@Override
public V get(K key) {
L secondKey = first.get(key);
return secondKey != null ? second.get(secondKey) : null;
}
}
package pl.lamvak.utils;
public class DefaultValueRepository <K, V> implements Repository<K, V> {
private Repository<K, V> repository;
private V defaultValue;
public DefaultValueRepository(Repository<K, V> repository, V defaultValue) {
this.repository = repository;
this.defaultValue = defaultValue;
}
@Override
public V get(K key) {
V value = repository.get(key);
return value == null ? defaultValue : value;
}
}
package pl.lamvak.utils;
import java.util.Collection;
public class FallbackRepository <K, V> implements Repository<K, V> {
private Collection<Repository<K, V>> repositories;
public FallbackRepository(Collection<Repository<K, V>> repositories) {
this.repositories = repositories;
}
@Override
public V get(K key) {
for (Repository<K, V> repository : repositories) {
V value = repository.get(key);
if (value != null) {
return value;
}
}
return null;
}
}
package pl.lamvak.utils;
import java.util.LinkedHashMap;
public class LRUCache <K, V> implements pl.lamvak.utils.Repository<K, V> {
private final Repository<K, V> repository;
private final int capacityLimit;
private final LinkedHashMap<K, V> cache;
public LRUCache(Repository<K, V> repository, int capacityLimit) {
this.repository = repository;
this.capacityLimit = capacityLimit;
cache = new LinkedHashMap<>();
}
public void clear() {
cache.clear();
}
@Override
public V get(K key) {
V value = cache.get(key);
if (value == null) {
value = repository.get(key);
} else {
cache.remove(key);
}
if (value == null) {
return null;
}
cache.put(key, value);
if (cache.size() > capacityLimit) {
cache.remove(leastRecentlyUsedKey());
}
return value;
}
private K leastRecentlyUsedKey() {
return cache.keySet().iterator().next();
}
}
package pl.lamvak.utils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.LinkedHashMap;
import static java.util.stream.IntStream.of;
import static java.util.stream.IntStream.rangeClosed;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class LRUCacheTest {
@Mock
private Repository<String, Integer> repositoryMock;
private LRUCache<String, Integer> cache;
private int capacityLimit = 3;
@Before
public void setup() {
this.cache = new LRUCache<>(repositoryMock, capacityLimit);
reset(repositoryMock);
}
@Test
public void cachesResults() {
when(repositoryMock.get("1")).thenReturn(1, 2, 3, 4, 5, 6);
Integer []values = new Integer[10];
rangeClosed(0, 9).forEach(i -> values[i] = cache.get("1"));
rangeClosed(0, 9).forEach(
i -> assertEquals((Integer)1, values[i]));
verify(repositoryMock, times(1)).get("1");
verifyNoMoreInteractions(repositoryMock);
}
@Test
public void testEviction() throws NoSuchFieldException, IllegalAccessException {
rangeClosed(1, 5).forEach(
i -> when(repositoryMock.get(Integer.toString(i))).thenReturn(i));
rangeClosed(1, 5).forEach(
i -> cache.get(Integer.toString(i)));
assertEquals(capacityLimit, cacheSize());
}
@Test
public void testLeastRecentlyUsedBehavior() throws NoSuchFieldException, IllegalAccessException {
when(repositoryMock.get(any())).thenReturn(1, 2, 3, 2, 1, 2);
of(1, 2, 3, 2, 1, 2).forEach(
i -> cache.get(Integer.toString(i)));
Iterator keys = keys();
of(3, 1, 2).forEach(i -> assertEquals(Integer.toString(i), keys.next()));
}
private int cacheSize() throws NoSuchFieldException, IllegalAccessException {
Field mapField = this.cache.getClass().getDeclaredField("cache");
mapField.setAccessible(true);
return ((LinkedHashMap) mapField.get(cache)).size();
}
private Iterator keys() throws NoSuchFieldException, IllegalAccessException {
Field mapField = this.cache.getClass().getDeclaredField("cache");
mapField.setAccessible(true);
return ((LinkedHashMap) mapField.get(cache)).keySet().iterator();
}
}
package pl.lamvak.utils;
public class SynchronizedRepository <K, V> implements Repository<K, V> {
private Repository<K, V> repository;
public SynchronizedRepository(Repository<K, V> repository) {
this.repository = repository;
}
@Override
synchronized public V get(K key) {
return repository.get(key);
}
}
package pl.lamvak.utils;
import javax.inject.Provider;
public class ThreadLocalRepository <K, V> implements Repository<K, V> {
ThreadLocal<Repository<K, V>> localRepository;
public ThreadLocalRepository(Provider<Repository<K, V>> repositoryProvider) {
localRepository = new ThreadLocal<Repository<K, V>>() {
@Override
public Repository<K, V> get() {
return repositoryProvider.get();
}
};
}
@Override
public V get(K key) {
return localRepository.get().get(key);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment