Make data "Expire"

Discussion in 'Spigot Plugin Development' started by Jonnyo101, May 1, 2017.

  1. Hi i am currently using a
    hashmap<String, Long> PlayerDelay

    Is will store a time stamp of when they last did something. Is there a way to make a player key and value get removed if it has not been accessed in x time ?
     
  2. Sure either when the command is run again check the delay

    Long delay = yourMap.get(string);

    If((system.currentTimeMills() - delay) >= someAmountOfTime)
    Remove it.

    Or you could have a timer that runs every so often depending on how long your delays are. It could be once every ten seconds or whatever number you like.

    Iterate over the whole HashMap and check the delays, if current time - the stored delay > some number remove it


    Sent from my iPhone using Tapatalk
     
  3. I do not mean that. I know how to do times delays that is not what i am asking.

    I want something like this

    ExpireHashMap<String, int> TimeStamps = new ExpireHashMap(10000);

    10000 being the time a entry will expire is not bee access by the plugin.

    So i had added to the ExpireHashMap
    - "Bobby", 1000
    - "Timmy", 1000

    If i have not used "Timmy" in the time given it will auto be cleared from the map.

    Maybe something like... https://gist.github.com/pcan/16faf4e59942678377e0

    But i am looking for a native way to do it.
     
  4. Sure you could use that class but a simple for loop to check if it has expired seems simpler than implementing a whole class which basically does the same thing.

    yourMap.put("Timmy" , System.currentTimeMills() + 10000)

    Would save a timestamp 10 seconds in the future. Meaning you would have 10 seconds to update that time before it would be invalid.

    Elsewhere in a timer (or whenever you update the HashMap again)

    Set<String> expired = new HashSet<String>()

    For(String s:yourMap.keySet())
    If(System.CurrentTimeMills() >= yourMap.get(s))
    expired.add(s);
    //this bit checks to see if the current time is >= the expire time, unless you update it that's 10 seconds from when it was added


    yourMap.removeAll(expired);


    Then do your updating / adding as you are now.


    Sent from my iPhone using Tapatalk
     
  5. Well, you can also use google cache in order to do this.
    Code (Java):

    Cache<String, Long> cache = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build();
     
    Also i made this class some time ago, it works quite well, but you will have to import it to Spigot, very simply anyways, also you could use a ConcurrentHashMap so you don't have to add all those synchronized blocks.
    Code (Java):

    public class LimitedDurationMap<K, V> {

        private final Map<K, CachedValue<V>> map = new HashMap<>();
        private final List<ExpirationListener<V>> listeners = new ArrayList<>();

        private final Plugin plugin;
        private final TimeUnit timeUnit;
        private final long time;
        private final long millisTime;

        public LimitedDurationMap(final Plugin plugin, final TimeUnit timeUnit, final long time) {
            this.plugin = plugin;
            this.timeUnit = timeUnit;
            this.time = time;
            this.millisTime = timeUnit.toMillis(time);

            ProxyServer.getInstance().getScheduler().schedule(plugin, new Runnable() {
                @Override
                public void run() {
                    cleanup();
                }
            }, time, time, timeUnit);
        }

        public Plugin getPlugin() {
            return plugin;
        }

        public TimeUnit getTimeUnit() {
            return timeUnit;
        }

        public long getTime() {
            return time;
        }

        public long getDuration() {
            return  millisTime;
        }

        public void put(K key, V value) {
            synchronized (map) {
                map.put(key, new CachedValue<>(value));
            }
        }

        public V get(K key) {
            synchronized (map) {
                CachedValue<V> value = map.get(key);

                if (value == null) {
                    return null;
                }

                if (value.expired()) {
                    call(value.getValue());
                    map.remove(key);
                } else {
                    return value.getValue();
                }

                return null;
            }
        }

        public boolean containsKey(K key) {
            return get(key) != null;
        }

        public void remove(K key) {
            synchronized (map) {
                map.remove(key);
            }
        }

        public void cleanup() {
            synchronized (map) {
                Iterator<Entry<K, CachedValue<V>>> iterator = map.entrySet().iterator();
                while (iterator.hasNext()) {
                    Entry<K, CachedValue<V>> entry = iterator.next();

                    if(entry.getValue().expired()) {
                        call(entry.getValue().getValue());

                        iterator.remove();
                    }
                }
            }
        }

        public Collection<V> getValues() {
            synchronized (map) {
                Collection<V> values = new ArrayList<>();

                Iterator<Entry<K, CachedValue<V>>> iterator = map.entrySet().iterator();
                while (iterator.hasNext()) {
                    Entry<K, CachedValue<V>> entry = iterator.next();

                    if(entry.getValue().expired()) {
                        call(entry.getValue().getValue());

                        iterator.remove();
                    } else {
                        values.add(entry.getValue().getValue());
                    }
                }

                return values;
            }
        }

        public void clear() {
            synchronized (map) {
                map.clear();
            }
        }

        public void addExpirationListener(ExpirationListener<V> expirationListener) {
            synchronized (listeners) {
                listeners.add(expirationListener);
            }
        }

        public synchronized void removeExpirationListener(ExpirationListener<V> expirationListener) {
            synchronized (listeners) {
                listeners.remove(expirationListener);
            }
        }

        public synchronized Collection<ExpirationListener<V>> getExpirationListeners() {
            return this.listeners;
        }

        private void call(V value) {
            if(value == null) return;
            for(ExpirationListener<V> listener : this.listeners) listener.onExpiration(value);
        }

        private class CachedValue<T> {

            private final T value;
            private long lastAccess;

            public CachedValue(T value) {
                this.value = value;
                this.lastAccess = System.currentTimeMillis();
            }

            public T getValue() {
                lastAccess = System.currentTimeMillis();
                return value;
            }

            public long getLastAccess() {
                return lastAccess;
            }

            public boolean expired() {
                return (lastAccess + getDuration()) - System.currentTimeMillis() <= 0;
            }

            @Override
            public boolean equals(Object object) {
                if(object == this) {
                    return true;
                } else if(object instanceof CachedValue){
                    return Objects.equals(value, ((CachedValue) object).value); //IDK if this can cause errors
                }

                return false;
            }

            @Override
            public int hashCode() {
                return Objects.hashCode(value);
            }
        }
    }
     
    Code (Java):

    public interface ExpirationListener<T> {

        void onExpiration(T value);

    }