package nl.rutgerkok.blocklocker.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;

import org.bukkit.block.Block;
import org.bukkit.plugin.Plugin;

import nl.rutgerkok.blocklocker.HopperCache;

final class HopperCacheImpl implements HopperCache {

    private static class CacheContainer {

        final long creationTimeMillis;
        final boolean isLocked;

        public CacheContainer(boolean locked, long time) {
            this.isLocked = locked;
            this.creationTimeMillis = time;
        }

        boolean isExpired(long currentTimeMillis) {
            return currentTimeMillis - this.creationTimeMillis > EXPIRE_TIME_SECONDS * 1000;
        }
    }

    private static final long EXPIRE_TIME_SECONDS = 10;
    private Map<Block, CacheContainer> accessCaching = new HashMap<>(1000);

    HopperCacheImpl(Plugin plugin) {
        plugin.getServer().getScheduler().runTaskTimer(plugin, this::cleanCache, EXPIRE_TIME_SECONDS * 20,
                EXPIRE_TIME_SECONDS * 20);
    }

    /**
     * Removes all expired entries.
     */
    private void cleanCache() {
        long currentTime = System.currentTimeMillis();
        Predicate<CacheContainer> isExpired = cacheEntry -> cacheEntry.isExpired(currentTime);
        accessCaching.values().removeIf(isExpired);
    }

    @Override
    public CacheFlag getIsRedstoneAllowed(Block block) {
        CacheContainer container;
        container = accessCaching.get(block);

        if (container == null) {
            return CacheFlag.MISS_CACHE;
        }
        if (container.isExpired(System.currentTimeMillis())) {
            return CacheFlag.MISS_CACHE;
        }
        if (container.isLocked) {
            return CacheFlag.PROTECTED;
        } else {
            return CacheFlag.NOT_PROTECTED;
        }
    }

    @Override
    public void setIsRedstoneAllowed(Block block, boolean locked) {
        accessCaching.put(block, new CacheContainer(locked, System.currentTimeMillis()));
    }
}

@@ -44,87 +44,129 @@
 ort 
-java.util.HashMap;%0Aimport java.util.Map;%0Aimport java.util.function.Predicat
+com.google.common.cache.Cache;%0Aimport com.google.common.cache.CacheBuilder;%0Aimport nl.rutgerkok.blocklocker.HopperCach
 e;%0A
-%0A
 impo
@@ -237,44 +237,37 @@
 ort 
-nl.rutgerkok.blocklocker.HopperCache
+java.util.concurrent.TimeUnit
 ;%0A%0Af
@@ -586,164 +586,8 @@
    %7D
-%0A%0A        boolean isExpired(long currentTimeMillis) %7B%0A            return currentTimeMillis - this.creationTimeMillis %3E EXPIRE_TIME_SECONDS * 1000;%0A        %7D
 %0A   
@@ -658,19 +658,21 @@
 private 
-Map
+Cache
 %3CBlock, 
@@ -704,30 +704,8 @@
 hing
- = new HashMap%3C%3E(1000)
 ;%0A%0A 
@@ -752,440 +752,203 @@
     
-plugin.getServer().getScheduler().runTaskTimer(plugin, this::cleanCache, EXPIRE_TIME_SECONDS * 20,%0A                EXPIRE_TIME_SECONDS * 2
+accessCaching = CacheBuilder.newBuilder().initialCapacity(1000)%0A                .maximumSize(500
 0)
-;
 %0A    
-%7D%0A%0A
     
-/**%0A
      
-* Removes all expired entries.%0A     */%0A    private void cleanCache() %7B%0A        long currentTime = System.currentTimeMillis();%0A        Predicate%3CCacheContainer%3E isExpired = cacheEntry -%3E cacheEntry.isExpired(currentTime);%0A        accessCaching.values().removeIf(isExpired
+   .expireAfterWrite(EXPIRE_TIME_SECONDS, TimeUnit.SECONDS)%0A                .build(
 );%0A 
@@ -1095,16 +1095,25 @@
 hing.get
+IfPresent
 (block);
@@ -1202,122 +1202,8 @@
   %7D%0A
-        if (container.isExpired(System.currentTimeMillis())) %7B%0A            return CacheFlag.MISS_CACHE;%0A        %7D%0A
     

Use Google Guava Cache to managing the caching to prevent heavy hit for sever performance when caches outdated and has been removed. (#64)