Last active
August 14, 2017 19:11
-
-
Save lamvak/24cc57ca37ebf3facd324cebe0d5ffcc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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