Skip to content

Instantly share code, notes, and snippets.

@pcan
Last active April 21, 2024 14:18
Show Gist options
  • Star 81 You must be signed in to star a gist
  • Fork 19 You must be signed in to fork a gist
  • Save pcan/16faf4e59942678377e0 to your computer and use it in GitHub Desktop.
Save pcan/16faf4e59942678377e0 to your computer and use it in GitHub Desktop.
SelfExpiringHashMap - a Java Map which entries expire automatically after a given time; it uses a DelayQueue internally.
/*
* Copyright (c) 2019 Pierantonio Cangianiello
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* A HashMap which entries expires after the specified life time.
* The life-time can be defined on a per-key basis, or using a default one, that is passed to the
* constructor.
*
* @author Pierantonio Cangianiello
* @param <K> the Key type
* @param <V> the Value type
*/
public class SelfExpiringHashMap<K, V> implements SelfExpiringMap<K, V> {
private final Map<K, V> internalMap;
private final Map<K, ExpiringKey<K>> expiringKeys;
/**
* Holds the map keys using the given life time for expiration.
*/
private final DelayQueue<ExpiringKey> delayQueue = new DelayQueue<ExpiringKey>();
/**
* The default max life time in milliseconds.
*/
private final long maxLifeTimeMillis;
public SelfExpiringHashMap() {
internalMap = new ConcurrentHashMap<K, V>();
expiringKeys = new WeakHashMap<K, ExpiringKey<K>>();
this.maxLifeTimeMillis = Long.MAX_VALUE;
}
public SelfExpiringHashMap(long defaultMaxLifeTimeMillis) {
internalMap = new ConcurrentHashMap<K, V>();
expiringKeys = new WeakHashMap<K, ExpiringKey<K>>();
this.maxLifeTimeMillis = defaultMaxLifeTimeMillis;
}
public SelfExpiringHashMap(long defaultMaxLifeTimeMillis, int initialCapacity) {
internalMap = new ConcurrentHashMap<K, V>(initialCapacity);
expiringKeys = new WeakHashMap<K, ExpiringKey<K>>(initialCapacity);
this.maxLifeTimeMillis = defaultMaxLifeTimeMillis;
}
public SelfExpiringHashMap(long defaultMaxLifeTimeMillis, int initialCapacity, float loadFactor) {
internalMap = new ConcurrentHashMap<K, V>(initialCapacity, loadFactor);
expiringKeys = new WeakHashMap<K, ExpiringKey<K>>(initialCapacity, loadFactor);
this.maxLifeTimeMillis = defaultMaxLifeTimeMillis;
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
cleanup();
return internalMap.size();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
cleanup();
return internalMap.isEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsKey(Object key) {
cleanup();
return internalMap.containsKey((K) key);
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsValue(Object value) {
cleanup();
return internalMap.containsValue((V) value);
}
@Override
public V get(Object key) {
cleanup();
renewKey((K) key);
return internalMap.get((K) key);
}
/**
* {@inheritDoc}
*/
@Override
public V put(K key, V value) {
return this.put(key, value, maxLifeTimeMillis);
}
/**
* {@inheritDoc}
*/
@Override
public V put(K key, V value, long lifeTimeMillis) {
cleanup();
ExpiringKey delayedKey = new ExpiringKey(key, lifeTimeMillis);
ExpiringKey oldKey = expiringKeys.put((K) key, delayedKey);
if(oldKey != null) {
expireKey(oldKey);
expiringKeys.put((K) key, delayedKey);
}
delayQueue.offer(delayedKey);
return internalMap.put(key, value);
}
/**
* {@inheritDoc}
*/
@Override
public V remove(Object key) {
V removedValue = internalMap.remove((K) key);
expireKey(expiringKeys.remove((K) key));
return removedValue;
}
/**
* Not supported.
*/
@Override
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public boolean renewKey(K key) {
ExpiringKey<K> delayedKey = expiringKeys.get((K) key);
if (delayedKey != null) {
delayedKey.renew();
return true;
}
return false;
}
private void expireKey(ExpiringKey<K> delayedKey) {
if (delayedKey != null) {
delayedKey.expire();
cleanup();
}
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
delayQueue.clear();
expiringKeys.clear();
internalMap.clear();
}
/**
* Not supported.
*/
@Override
public Set<K> keySet() {
throw new UnsupportedOperationException();
}
/**
* Not supported.
*/
@Override
public Collection<V> values() {
throw new UnsupportedOperationException();
}
/**
* Not supported.
*/
@Override
public Set<Entry<K, V>> entrySet() {
throw new UnsupportedOperationException();
}
private void cleanup() {
ExpiringKey<K> delayedKey = delayQueue.poll();
while (delayedKey != null) {
internalMap.remove(delayedKey.getKey());
expiringKeys.remove(delayedKey.getKey());
delayedKey = delayQueue.poll();
}
}
private class ExpiringKey<K> implements Delayed {
private long startTime = System.currentTimeMillis();
private final long maxLifeTimeMillis;
private final K key;
public ExpiringKey(K key, long maxLifeTimeMillis) {
this.maxLifeTimeMillis = maxLifeTimeMillis;
this.key = key;
}
public K getKey() {
return key;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ExpiringKey<K> other = (ExpiringKey<K>) obj;
if (this.key != other.key && (this.key == null || !this.key.equals(other.key))) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + (this.key != null ? this.key.hashCode() : 0);
return hash;
}
/**
* {@inheritDoc}
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(getDelayMillis(), TimeUnit.MILLISECONDS);
}
private long getDelayMillis() {
return (startTime + maxLifeTimeMillis) - System.currentTimeMillis();
}
public void renew() {
startTime = System.currentTimeMillis();
}
public void expire() {
startTime = Long.MIN_VALUE;
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(Delayed that) {
return Long.compare(this.getDelayMillis(), ((ExpiringKey) that).getDelayMillis());
}
}
}
/*
* Copyright (c) 2019 Pierantonio Cangianiello
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import it.pcan.util.map.SelfExpiringHashMap;
import it.pcan.util.map.SelfExpiringMap;
import static org.junit.Assert.*;
import org.junit.Test;
/**
*
* @author Pierantonio Cangianiello
*/
public class SelfExpiringHashMapTests {
private final static int SLEEP_MULTIPLIER = 80;
@Test
public void basicGetTest() throws InterruptedException {
SelfExpiringMap<String, String> map = new SelfExpiringHashMap<String, String>();
map.put("a", "b", 2 * SLEEP_MULTIPLIER);
Thread.sleep(1 * SLEEP_MULTIPLIER);
assertEquals("b", map.get("a"));
}
@Test
public void basicExpireTest() throws InterruptedException {
SelfExpiringMap<String, String> map = new SelfExpiringHashMap<String, String>();
map.put("a", "b", 2 * SLEEP_MULTIPLIER);
Thread.sleep(3 * SLEEP_MULTIPLIER);
assertNull(map.get("a"));
}
@Test
public void basicRenewTest() throws InterruptedException {
SelfExpiringMap<String, String> map = new SelfExpiringHashMap<String, String>();
map.put("a", "b", 3 * SLEEP_MULTIPLIER);
Thread.sleep(2 * SLEEP_MULTIPLIER);
map.renewKey("a");
Thread.sleep(2 * SLEEP_MULTIPLIER);
assertEquals("b", map.get("a"));
}
@Test
public void getRenewTest() throws InterruptedException {
SelfExpiringMap<String, String> map = new SelfExpiringHashMap<String, String>();
map.put("a", "b", 3 * SLEEP_MULTIPLIER);
Thread.sleep(2 * SLEEP_MULTIPLIER);
assertEquals("b", map.get("a"));
Thread.sleep(2 * SLEEP_MULTIPLIER);
assertEquals("b", map.get("a"));
}
@Test
public void multiplePutThenRemoveTest() throws InterruptedException {
SelfExpiringMap<String, String> map = new SelfExpiringHashMap<String, String>();
map.put("a", "b", 2 * SLEEP_MULTIPLIER);
Thread.sleep(1 * SLEEP_MULTIPLIER);
map.put("a", "c", 2 * SLEEP_MULTIPLIER);
Thread.sleep(1 * SLEEP_MULTIPLIER);
map.put("a", "d", 400 * SLEEP_MULTIPLIER);
Thread.sleep(2 * SLEEP_MULTIPLIER);
assertEquals("d", map.remove("a"));
}
@Test
public void multiplePutThenGetTest() throws InterruptedException {
SelfExpiringMap<String, String> map = new SelfExpiringHashMap<String, String>();
map.put("a", "b", 2 * SLEEP_MULTIPLIER);
Thread.sleep(1 * SLEEP_MULTIPLIER);
map.put("a", "c", 2 * SLEEP_MULTIPLIER);
Thread.sleep(1 * SLEEP_MULTIPLIER);
map.put("a", "d", 400 * SLEEP_MULTIPLIER);
Thread.sleep(2 * SLEEP_MULTIPLIER);
assertEquals("d", map.get("a"));
}
@Test
public void insertionOrderTest() throws InterruptedException {
SelfExpiringMap<String, Integer> map = new SelfExpiringHashMap<String, Integer>(30000);
map.put("123456", 999);
assertEquals(map.get("123456"), Integer.valueOf(999));
map.put("123456", 123);
map.put("777456", 333);
assertEquals(map.get("123456"), Integer.valueOf(123));
assertEquals(map.get("777456"), Integer.valueOf(333));
map.put("777456", 123);
map.put("123456", 321);
assertEquals(map.get("123456"), Integer.valueOf(321));
assertEquals(map.get("777456"), Integer.valueOf(123));
}
}
/*
* Copyright (c) 2019 Pierantonio Cangianiello
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import java.util.Map;
/**
*
* @author Pierantonio Cangianiello
* @param <K> the Key type
* @param <V> the Value type
*/
public interface SelfExpiringMap<K, V> extends Map<K, V> {
/**
* Renews the specified key, setting the life time to the initial value.
*
* @param key
* @return true if the key is found, false otherwise
*/
public boolean renewKey(K key);
/**
* Associates the given key to the given value in this map, with the specified life
* times in milliseconds.
*
* @param key
* @param value
* @param lifeTimeMillis
* @return a previously associated object for the given key (if exists).
*/
public V put(K key, V value, long lifeTimeMillis);
}
@yourlearning
Copy link

Under what condition (license) I can re/use it?

Thanks,
Pavel

@pcan
Copy link
Author

pcan commented Feb 27, 2017

Hi thank you for your interest. This content is covered by the MIT License. I have updated the gist with license information.

@patricerosay
Copy link

patricerosay commented Jul 11, 2017

Hi.
I'm trying to use your class that way.
I assume to have at most 3 items in the map, each of them containing at most 10 elements.
It works well at the beginning, but after while, results are unpredictable.
Is there an issue in SelfExpiringMap class?
Please advise, Thanks

Here's the my test code I'm executing in the SelfExpiringMapTests.java file:

Code
    @Test
public void nPutThenGetTest() throws InterruptedException {
	SelfExpiringMap<Long, BulkClass> cache = new SelfExpiringHashMap<Long, BulkClass>(2000);
	ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
	Runnable filler = new Runnable() {
		public void run() {
			try {
				long time = new Date().getTime() ;
				long id=time/ 1000;
				BulkClass old = cache.get(id);
				BulkClass nmeaData = null;
				if (null == old) {
					nmeaData = new BulkClass();
				} else {
					nmeaData = new BulkClass(old);
				}
				nmeaData.data.put("key"+time, "value"+time);
				cache.put(id, nmeaData);
				System.out.println("----------------------------");
				System.out.println(cache.size());
				System.out.println(cache);
				} catch (Exception e) {
			}
		}
	};
	ScheduledFuture<?> tackHandle = scheduler.scheduleAtFixedRate(filler, 0, 100, MILLISECONDS);
	Thread.sleep(5000);
}
class BulkClass {
	HashMap<String, String> data = null;
	BulkClass() {
		data = new HashMap<String, String>();
	}
	BulkClass(BulkClass d) {
		data = (HashMap<String, String>) d.data.clone();
	}
	public String toString()
	{
		String ret="data size:"+data.size();
		for(String k:data.keySet())
		{
			ret+=" key:"+k+" value:"+data.get(k);
		}
		return ret;
	}
};

To have a better trace, i've added a toString in SelfExpiringMap.java file:

Code
public String toString()
{
	String ret="";
	for (Map.Entry<K,V> entry : internalMap.entrySet()) {
	    K k = entry.getKey();
	    V v = entry.getValue();
	    ret+= "k:"+k+ " entry:"+v+"\n\r";
	}
	return ret;
}

And here's the results

Results ---------------------------- 1 k:1499768735 entry:data size:1 key:key1499768735192 value:value1499768735192

removing 1499768735

1
k:1499768735 entry:data size:2 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192

removing 1499768735

1
k:1499768735 entry:data size:3 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192

removing 1499768735

1
k:1499768735 entry:data size:4 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192

removing 1499768735

1
k:1499768735 entry:data size:5 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192

removing 1499768735

1
k:1499768735 entry:data size:6 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192

removing 1499768735

1
k:1499768735 entry:data size:7 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735794 value:value1499768735794

removing 1499768735

1
k:1499768735 entry:data size:8 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735794 value:value1499768735794

removing 1499768735

1
k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:1 key:key1499768736092 value:value1499768736092

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:2 key:key1499768736092 value:value1499768736092 key:key1499768736193 value:value1499768736193

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:3 key:key1499768736092 value:value1499768736092 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:4 key:key1499768736092 value:value1499768736092 key:key1499768736394 value:value1499768736394 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:5 key:key1499768736092 value:value1499768736092 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:6 key:key1499768736092 value:value1499768736092 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:7 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:8 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:9 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


2
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:1 key:key1499768737093 value:value1499768737093

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:2 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:3 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:4 key:key1499768737392 value:value1499768737392 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:5 key:key1499768737392 value:value1499768737392 key:key1499768737492 value:value1499768737492 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:6 key:key1499768737392 value:value1499768737392 key:key1499768737492 value:value1499768737492 key:key1499768737592 value:value1499768737592 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:7 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292 key:key1499768737392 value:value1499768737392 key:key1499768737492 value:value1499768737492 key:key1499768737592 value:value1499768737592 key:key1499768737693 value:value1499768737693

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:8 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292 key:key1499768737392 value:value1499768737392 key:key1499768737492 value:value1499768737492 key:key1499768737592 value:value1499768737592 key:key1499768737693 value:value1499768737693 key:key1499768737793 value:value1499768737793

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:9 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292 key:key1499768737392 value:value1499768737392 key:key1499768737492 value:value1499768737492 key:key1499768737592 value:value1499768737592 key:key1499768737693 value:value1499768737693 key:key1499768737793 value:value1499768737793 key:key1499768737892 value:value1499768737892

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794


3
k:1499768736 entry:data size:10 key:key1499768736893 value:value1499768736893 key:key1499768736793 value:value1499768736793 key:key1499768736693 value:value1499768736693 key:key1499768736494 value:value1499768736494 key:key1499768736394 value:value1499768736394 key:key1499768736592 value:value1499768736592 key:key1499768736193 value:value1499768736193 key:key1499768736292 value:value1499768736292 key:key1499768736092 value:value1499768736092 key:key1499768736993 value:value1499768736993

k:1499768737 entry:data size:10 key:key1499768737093 value:value1499768737093 key:key1499768737193 value:value1499768737193 key:key1499768737292 value:value1499768737292 key:key1499768737392 value:value1499768737392 key:key1499768737492 value:value1499768737492 key:key1499768737592 value:value1499768737592 key:key1499768737693 value:value1499768737693 key:key1499768737793 value:value1499768737793 key:key1499768737892 value:value1499768737892 key:key1499768737993 value:value1499768737993

k:1499768735 entry:data size:9 key:key1499768735892 value:value1499768735892 key:key1499768735693 value:value1499768735693 key:key1499768735593 value:value1499768735593 key:key1499768735492 value:value1499768735492 key:key1499768735392 value:value1499768735392 key:key1499768735292 value:value1499768735292 key:key1499768735192 value:value1499768735192 key:key1499768735994 value:value1499768735994 key:key1499768735794 value:value1499768735794

=>Starting from here, results are unpredictable:
removing 1499768735
removing 1499768736
removing 1499768736
removing 1499768736
removing 1499768736
removing 1499768736
removing 1499768736
removing 1499768736
removing 1499768736
removing 1499768736
removing 1499768737
removing 1499768737
removing 1499768737
removing 1499768737
removing 1499768737
removing 1499768737
removing 1499768737
removing 1499768737
removing 1499768737

1
k:1499768738 entry:data size:1 key:key1499768738094 value:value1499768738094


1
k:1499768738 entry:data size:2 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094


1
k:1499768738 entry:data size:3 key:key1499768738294 value:value1499768738294 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094


1
k:1499768738 entry:data size:4 key:key1499768738294 value:value1499768738294 key:key1499768738393 value:value1499768738393 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094


1
k:1499768738 entry:data size:5 key:key1499768738294 value:value1499768738294 key:key1499768738393 value:value1499768738393 key:key1499768738493 value:value1499768738493 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094


1
k:1499768738 entry:data size:6 key:key1499768738294 value:value1499768738294 key:key1499768738393 value:value1499768738393 key:key1499768738493 value:value1499768738493 key:key1499768738592 value:value1499768738592 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094


1
k:1499768738 entry:data size:7 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094 key:key1499768738294 value:value1499768738294 key:key1499768738393 value:value1499768738393 key:key1499768738493 value:value1499768738493 key:key1499768738592 value:value1499768738592 key:key1499768738694 value:value1499768738694


1
k:1499768738 entry:data size:8 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094 key:key1499768738294 value:value1499768738294 key:key1499768738393 value:value1499768738393 key:key1499768738493 value:value1499768738493 key:key1499768738592 value:value1499768738592 key:key1499768738694 value:value1499768738694 key:key1499768738793 value:value1499768738793


1
k:1499768738 entry:data size:9 key:key1499768738192 value:value1499768738192 key:key1499768738094 value:value1499768738094 key:key1499768738294 value:value1499768738294 key:key1499768738393 value:value1499768738393 key:key1499768738493 value:value1499768738493 key:key1499768738592 value:value1499768738592 key:key1499768738694 value:value1499768738694 key:key1499768738793 value:value1499768738793 key:key1499768738894 value:value1499768738894

removing 1499768736
removing 1499768738
removing 1499768738
removing 1499768738
removing 1499768738
removing 1499768738
removing 1499768738
removing 1499768738
removing 1499768738

1
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:1 key:key1499768739093 value:value1499768739093


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:2 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:3 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:4 key:key1499768739394 value:value1499768739394 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:5 key:key1499768739394 value:value1499768739394 key:key1499768739494 value:value1499768739494 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:6 key:key1499768739394 value:value1499768739394 key:key1499768739494 value:value1499768739494 key:key1499768739594 value:value1499768739594 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:7 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293 key:key1499768739394 value:value1499768739394 key:key1499768739692 value:value1499768739692 key:key1499768739494 value:value1499768739494 key:key1499768739594 value:value1499768739594


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:8 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293 key:key1499768739394 value:value1499768739394 key:key1499768739692 value:value1499768739692 key:key1499768739494 value:value1499768739494 key:key1499768739594 value:value1499768739594 key:key1499768739792 value:value1499768739792


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:9 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293 key:key1499768739394 value:value1499768739394 key:key1499768739692 value:value1499768739692 key:key1499768739494 value:value1499768739494 key:key1499768739594 value:value1499768739594 key:key1499768739892 value:value1499768739892 key:key1499768739792 value:value1499768739792


2
k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:10 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293 key:key1499768739394 value:value1499768739394 key:key1499768739692 value:value1499768739692 key:key1499768739494 value:value1499768739494 key:key1499768739594 value:value1499768739594 key:key1499768739892 value:value1499768739892 key:key1499768739792 value:value1499768739792 key:key1499768739992 value:value1499768739992

removing 1499768737

3
k:1499768740 entry:data size:1 key:key1499768740094 value:value1499768740094

k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:10 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293 key:key1499768739394 value:value1499768739394 key:key1499768739692 value:value1499768739692 key:key1499768739494 value:value1499768739494 key:key1499768739594 value:value1499768739594 key:key1499768739892 value:value1499768739892 key:key1499768739792 value:value1499768739792 key:key1499768739992 value:value1499768739992


3
k:1499768740 entry:data size:2 key:key1499768740094 value:value1499768740094 key:key1499768740193 value:value1499768740193

k:1499768738 entry:data size:1 key:key1499768738993 value:value1499768738993

k:1499768739 entry:data size:10 key:key1499768739093 value:value1499768739093 key:key1499768739193 value:value1499768739193 key:key1499768739293 value:value1499768739293 key:key1499768739394 value:value1499768739394 key:key1499768739692 value:value1499768739692 key:key1499768739494 value:value1499768739494 key:key1499768739594 value:value1499768739594 key:key1499768739892 value:value1499768739892 key:key1499768739792 value:value1499768739792 key:key1499768739992 value:value1499768739992

@pcan
Copy link
Author

pcan commented Jul 14, 2017

@patricerosay sorry for the delay, just now I'm trying to understand the goal of your test. Could you explain with a more succint example the issue you found? Actually, all that bunch of internal data (inner BulkClass) is not really relevant for the example, and clutters the logs so much. If you describe the desired behaviour as single steps (see my comment here for an example of how to explain a race-condition bug), with a pseudo-code language, it's simpler for me to understand your needs and the probable defect of the Expiring Map.
P.S. I edited your commed right now by adding collapsible sections, in order to increase readability.

@datnt1987
Copy link

datnt1987 commented Oct 24, 2017

Dear @pcan, currently I have a list with 10 elements (String) and after every one minute, the list remove a member. So please, any suggestion with using SelfExpiringMap/SelfExpiringHashmap ??? Thanks

@pcan
Copy link
Author

pcan commented Oct 24, 2017

Hi @datnt1987, if I understood well, you need a list implementation of expiring items, right? If so, I think that SelfExpiringMap could not be used as is, but you could think an implementation of List interface that internally uses a LinkedList (for efficient inner items removal) and a delay queue that returns the index of each item to be removed. Then, each List method (eg. add, get, and so on) should call a cleanup() method that polls the DelayQueue for expired items indexes and cleans up the internal LinkedList before doing anything else.

@dehad
Copy link

dehad commented Jan 12, 2018

Hi @pcan
How Can I use your software ? Is there binary artifact or Can I copy your code in my project?

@pcan
Copy link
Author

pcan commented Jan 12, 2018

Hi @dehad. At the moment, this implementation is not available on Maven, but you can copy the source code in your project. Please, just keep the copyright & license notice in the top comment. Thanks for your interest.

@VladimirKhizmatulin
Copy link

Code:
SelfExpiringHashMap selfExpiringHashMap = new SelfExpiringHashMap(300*1000);
selfExpiringHashMap.put(new Pair("123", "456"), 999);
System.out.println(selfExpiringHashMap);
selfExpiringHashMap.put(new Pair("123", "456"), 123);
selfExpiringHashMap.put(new Pair("777", "456"), 333);
System.out.println(selfExpiringHashMap);
selfExpiringHashMap.put(new Pair("777", "456"), 123);
selfExpiringHashMap.put(new Pair("123", "456"), 321);
System.out.println(selfExpiringHashMap);
Output:
SelfExpiringHashMap{internalMap={123=456=999}}
SelfExpiringHashMap{internalMap={123=456=123, 777=456=333}}
SelfExpiringHashMap{internalMap={123=456=321}}
Seems buggy. Now I use org.apache.commons.collections4.map.PassiveExpiringMap instead.

@pcan
Copy link
Author

pcan commented May 16, 2018

Hi @VladimirKhizmatulin, thanks for reporting this issue. I fixed it (it was a wrong time setting on key expiration). I updated the tests too. Beware that PassiveExpiringMap does a full map scan when it removes expired entries; my ExpiringEntriesMap, instead, just iterates the expired keys because it uses a DelayQueue.

@ivailokolev
Copy link

Hello Pierantonio, would you please elaborate on the usage of the WeakHashMap. Aren't there concurrency concerns, as its not thread-safe? What is the expected benefit?

@hleofxquotes
Copy link

Hi Pierantonio, thanks for making your codes available. Just want to double-check. Look like get() will renew the key, yes? Per test case

    @Test
    public void getRenewTest() throws InterruptedException {
        SelfExpiringMap<String, String> map = new SelfExpiringHashMap<String, String>();
        map.put("a", "b", 3 * SLEEP_MULTIPLIER);
        Thread.sleep(2 * SLEEP_MULTIPLIER);
        Assert.assertEquals("b", map.get("a"));
        Thread.sleep(2 * SLEEP_MULTIPLIER);
        Assert.assertEquals("b", map.get("a"));
    }

For my use case, I prefer not to "renew" on each get. Is there any possible side effect if I just skip the renewKey() call in get()?

    private boolean renewKeyOnGet = false;

    @Override
    public V get(Object key) {
        cleanup();
        if (renewKeyOnGet) {
            renewKey((K) key);
        }
        return internalMap.get((K) key);
    }

@pcan
Copy link
Author

pcan commented Aug 30, 2018

Hello Pierantonio, would you please elaborate on the usage of the WeakHashMap. Aren't there concurrency concerns, as its not thread-safe? What is the expected benefit?

This class has not been tested in a multithreading environment. However, you may use a synchronizer wrapper like Collections.synchronizedMap(). The main benefit of this implementation is the absence of a backing thread that cleans up the map, since I use a DelayQueue here.

Hi Pierantonio, thanks for making your codes available. Just want to double-check. Look like get() will renew the key, yes?

Hi, thank you for your interest 😃
Yes, at the moment the Expiring Map works like a standard "cache", but if you need more control you may add the renewKeyOnGet boolean, of course. I don't think there are unwanted side effects, but I suggest to write some additional unit tests.

@prasanthmp500
Copy link

@pcan
Copy link
Author

pcan commented Oct 19, 2018

i have one used the selfexpiring hash map. it is very buggy . use this https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/PassiveExpiringMap.html

Hi @prasanthmp500. Thank you for your comment. I would just ask you two things:

  1. You have been pretty vague by saying "it is very buggy". Could you report your failed test case(s), please?
  2. Did you know that Apache PassiveExpiringMap always does a full map scan for each operation? It's very, very inefficient for the medium usage (except when building a toy use-case). This implementation uses an high-efficient priority queue based on entries delay.

Said that, please note that SelfExpiringHashMap has been in a true production environment for many years, till now without any issue.

@petersaitz
Copy link

There is a bug in renewKey(K key)
In the if (delayedKey != null) condition, the following 2 lines are required to move the renewed element to the end of the queue.
delayedKey.renew();
delayQueue.remove(delayedKey); // added
delayQueue.add(delayedKey); // added

@nimbusLiQuid
Copy link

There is a bug in renewKey(K key)
In the if (delayedKey != null) condition, the following 2 lines are required to move the renewed element to the end of the queue.
delayedKey.renew();
delayQueue.remove(delayedKey); // added
delayQueue.add(delayedKey); // added

Can you explain why this is needed?

i have one used the selfexpiring hash map. it is very buggy . use this https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/PassiveExpiringMap.html

Hi @prasanthmp500. Thank you for your comment. I would just ask you two things:

  1. You have been pretty vague by saying "it is very buggy". Could you report your failed test case(s), please?
  2. Did you know that Apache PassiveExpiringMap always does a full map scan for each operation? It's very, very inefficient for the medium usage (except when building a toy use-case). This implementation uses an high-efficient priority queue based on entries delay.

Said that, please note that PassiveExpiringMap has been in a true production environment for many years, till now without any issue.

I am considering using your gist, thank you!

@ignaciorecuerda
Copy link

Can I use it importing it as a library?

@pcan
Copy link
Author

pcan commented Aug 9, 2019

Can I use it importing it as a library?

@ignaciorecuerda currently this class is not part of any library, you can copy the source code directly in your project; just keep the included license information comments.
Thank you for your interest.

@ignaciorecuerda
Copy link

Thank you @pcan

@prameeshas
Copy link

prameeshas commented Sep 6, 2019

Hi,
While testing out this code, I came across a test case that was not giving me the expected result. I noticed that if the first element is renewed, the second element even if expired, will not be removed from the map. If the order of initial insert are swapped, the test case passes. I had to either remove the renew method, which was called from the get method or remove the element and re-insert it to the map in a synchronized method to ensure all the keys expire in the cleanup method as expected. Is this the correct way to do this?

@Test
public void multiplePutTillExpireTest() throws InterruptedException {
    SelfExpiringMap<String, String> map = new SelfExpiringHashMap<>(60000);
    map.put("Test1.txt", "c");
    map.put("Test2.txt", "b");
    Thread.sleep(50*1000);
    if(map.containsKey("Test1.txt"))
        map.get("Test1.txt");
    Thread.sleep(40*1000);
    assertFalse(map.containsKey("Test2.txt"));

}

@pcan
Copy link
Author

pcan commented Sep 7, 2019

Hi @prameeshas, this issue seems related to the DelayQueue internals, since it lacks an efficient mechanism for key renewal. I'm going to fix the implementation in order to make your test case pass. Thank you for reporting this issue.

@fdummert
Copy link

Hi @pcan, the current implementation seems to leak elements in the DelayQueue when removing them from the map before they expire. After e.g. invoking:

map.put("foo", 1);
map.remove("foo");

the SelfExpiringHashMap seems to be empty at first glance, but the DelayQueue still holds an ExpiringKey instance for the key "foo". The issue seems to come from the change to set startTime = Long.MIN_VALUE when expire() is called for this key and the subsequent calculation of getDelay(). Polling the queue fails then. After making ExpiringKey available to this small test by declaring it public static you can see that an expired key (what remove is essentially doing) cannot be polled from the queue:

@Test
void testDelay() {
    final DelayQueue<ExpiringKey<String>> queue = new DelayQueue<>();
    final ExpiringKey<String> key = new ExpiringKey<>("foo", 1000);
    queue.add(key);
    key.expire();
    assertNotNull(queue.poll());
}

@pcan
Copy link
Author

pcan commented Jul 15, 2020

hi @fdummert, basically this triggers the same issue of the one reported by prameeshas. I still haven't got time to fix this, but basically I will move the implementation to a RB-tree instead of a priority-queue based (as current DelayQueue implementation relies on).

@osmaninci
Copy link

osmaninci commented Jan 11, 2022

Hi @pcan , why SelfExpiringHashMap.values() method throw UnsupportedOperationException? Is there any specific reason? What if it is implemented like below

@OverRide
public Collection values() {
cleanup();
return internalMap.values();
}

@ducanh2110
Copy link

Hi @pcan , why method get(Object key) need to renewKey() again?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment