Объекты с истекающим сроком действия PassiveExpiringMap

#java #collections #apache-commons

#java #Коллекции #apache-commons

Вопрос:

Я ожидал, что org.apache.commons.collections4.map.PassiveExpiringMap будет работать из коробки. К сожалению, это не работает для меня в наиболее очевидном случае использования истекающих объектов вовремя. Приведенный ниже код создает политику истечения срока действия в 5 секунд. Тест ожидает 10 секунд, что является временем, к которому весь объект карты должен быть истек и удален. Но это не так.

 import org.apache.commons.collections4.map.PassiveExpiringMap;
import org.junit.Assert;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
    
public class PassiveExpiringMapTest {
    @Test
    public void givenDataMap_whenWrappingMapWithPassiveExpiringMap_thenObjectsAreRemovedWhenExpired() {
        
        final Map<String, Integer> m = new HashMap<>();
        m.put("one", Integer.valueOf(1));
        m.put("two", Integer.valueOf(2));
        m.put("three", Integer.valueOf(3));

        PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<String, Integer>
            expirationPolicy = new PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<>(
            5, TimeUnit.SECONDS);

        PassiveExpiringMap<String, Integer> expiringMap = new PassiveExpiringMap<>(expirationPolicy, m);

        int initialCapacity = expiringMap.size();
        System.out.println("initialCapacity = "   initialCapacity);
        Assert.assertEquals(3, initialCapacity);

        System.out.println("Sleeping...");
        try { Thread.sleep(10000L); } catch (InterruptedException e) { }

        int updatedCapacity = expiringMap.size();
        System.out.println("updatedCapacity = "   updatedCapacity);
        Integer one = expiringMap.get("one");
        Assert.assertNull(one);
        Assert.assertEquals(0, updatedCapacity);
    }
}
  

Тест завершается ошибкой со следующим выводом:

 initialCapacity = 3
Sleeping...
updatedCapacity = 3

java.lang.AssertionError: expected null, but was:<1>

    at org.junit.Assert.fail(Assert.java:88)
    at org.junit.Assert.failNotNull(Assert.java:755)
    at org.junit.Assert.assertNull(Assert.java:737)
    at org.junit.Assert.assertNull(Assert.java:747)
  

Есть идеи, чего мне не хватает?

Ответ №1:

Просмотр исходного кода PassiveExpiringMap помог мне понять, почему моя первоначальная версия не работала. Инициализация с использованием карты приводит к тому, что внутреннее поле expirationMap становится пустым. expirationMap должна содержать запись для каждого объекта, чтобы объекты с истекшим сроком действия были удалены. Для того, чтобы это произошло, необходимо использовать метод put . Приведенный ниже измененный код работает. Обратите внимание, что объекты добавляются в expiringMap напрямую, а не в HashMap, как раньше:

 import org.apache.commons.collections4.map.PassiveExpiringMap;
import org.junit.Assert;
import org.junit.Test;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;

public class PassiveExpiringMapTest {

    @Test
    public void givenDataMap_whenWrappingMapWithPassiveExpiringMap_thenObjectsAreRemovedWhenExpired() {
        
        PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<String, Integer>
            expirationPolicy = new PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<>(
            5, TimeUnit.SECONDS);

        PassiveExpiringMap<String, Integer> expiringMap = new PassiveExpiringMap<>(expirationPolicy, new HashMap<>());
        expiringMap.put("one", Integer.valueOf(1));
        expiringMap.put("two", Integer.valueOf(2));
        expiringMap.put("three", Integer.valueOf(3));
        int initialCapacity = expiringMap.size();
        System.out.println("initialCapacity = "   initialCapacity);
        Assert.assertEquals(3, initialCapacity);

        System.out.println("Sleeping...");
        try { Thread.sleep(10000L); } catch (InterruptedException e) { }

        int updatedCapacity = expiringMap.size();
        System.out.println("updatedCapacity = "   updatedCapacity);
        Integer one = expiringMap.get("one");
        Assert.assertNull(one);
        Assert.assertEquals(0, updatedCapacity);
    }
}
  

Ответ №2:

В вашем коде добавьте map ‘m’ в ‘expiringMap’

     PassiveExpiringMap<String, Integer> expiringMap = new PassiveExpiringMap<>(expirationPolicy,m);
    expiringMap.putAll(m);
  

Комментарии:

1. это работает, потому что он вызывает this.put() внутренне — см. Мой ответ.