Выполняет ли итерация над вводом-отображением карты Guava LoadCache набора reset времени доступа, используемого для истечения срока действия? - программирование
Подтвердить что ты не робот

Выполняет ли итерация над вводом-отображением карты Guava LoadCache набора reset времени доступа, используемого для истечения срока действия?

У меня есть LoadingCache<K,V>, созданный с помощью CacheBuilder

LoadingCache<K,V> myCache = CacheBuilder.newBuilder()
    .expireAfterAccess(1, TimeUnit.MINUTES)
    .maximumSize(500)
    .build(someCacheLoader);

Мне нужно периодически перебирать все записи (ключи и значения) в кеше. Я знаю, что могу сделать это, используя LoadingCache#asMap() и либо:

В первом случае:

for (Map.Entry<K, V> entry : myCache.asMap().entrySet()) {
    K key = entry.getKey();
    V value = entry.getValue();

    doSomeWorkOn(key, value);
}

Будет ли это обновлять время доступа каждой записи в кеше? Я довольно хорошо читал JavaDoc для CacheBuilder#expireAfterAccess(long, TimeUnit), но обнаружил, что это двусмысленно/неясно в этом случае:

Указывает, что каждая запись должна автоматически удаляться из кеша после истечения фиксированной продолжительности после создания записи, последней замены ее значения или последнего доступа. Время доступа reset выполняется всеми операциями чтения и записи кеша (включая Cache.asMap().get(Object) и Cache.asMap().put(K, V)), но не операциями над представлениями коллекции Cache.asMap.

Очевидно, что второй способ итерации, о котором я говорил, reset время доступа, но я хотел бы знать, что поведение использует первый способ.

4b9b3361

Ответ 1

Я бы это интерпретировал:

(...), но не операциями над представлениями коллекции Cache.asMap

для обозначения entrySet, keySet и values. Это три вида коллекции Map. Поэтому использование их не должно приводить к доступу.

Вот тесты JUnit (+ Mockito), которые показывают поведение в каждом случае. Чтение значения с помощью entrySet или values делает не предотвращение удаления записи (а также чтение ключа с entrySet или keySet). Чтение с использованием asMap().get() считается счетным, как указано в документах.

Настройка

private Ticker ticker = Mockito.mock(Ticker.class);

@SuppressWarnings({"unchecked"})
private RemovalListener<String, String> removalListener = Mockito.mock(RemovalListener.class);

private Cache<String, String> cache = CacheBuilder.newBuilder()
            .expireAfterAccess(5, TimeUnit.SECONDS)
            .removalListener(removalListener)
            .ticker(ticker)
            .build();

entrySet

@Test
public void testEntrySetAccessDoesNotCountAsAccess() {
    //write
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(0));
    cache.put("foo", "bar");

    //read
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(4));
    cache.asMap().entrySet().iterator().next().getValue();
    cache.asMap().entrySet().iterator().next().getKey();

    //maintenance
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(6));
    cache.cleanUp();

    verify(removalListener).onRemoval(Mockito.<RemovalNotification<String,String>>any());
}

keySet

@Test
public void testKeySetAccessDoesNotCountAsAccess() {
    //write
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(0));
    cache.put("foo", "bar");

    //read
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(4));
    cache.asMap().keySet().iterator().next();

    //maintenance
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(6));
    cache.cleanUp();

    verify(removalListener).onRemoval(Mockito.<RemovalNotification<String,String>>any());
}

values

@Test
public void testValuesAccessDoesNotCountAsAccess() {
    //write
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(0));
    cache.put("foo", "bar");

    //read
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(4));
    cache.asMap().values().iterator().next();

    //maintenance
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(6));
    cache.cleanUp();

    verify(removalListener).onRemoval(Mockito.<RemovalNotification<String,String>>any());
}

asMap().get()

@Test
public void testMapGetAccessCountsAsAccess() {
    //write
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(0));
    cache.put("foo", "bar");

    //read
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(4));
    cache.asMap().get("foo");

    //maintenance
    when(ticker.read()).thenReturn(TimeUnit.SECONDS.toNanos(6));
    cache.cleanUp();

    verify(removalListener, never()).onRemoval(Mockito.<RemovalNotification<String,String>>any());
}