代码之家  ›  专栏  ›  技术社区  ›  Sean Patrick Floyd

基于Java时间的map/cache和过期键[closed]

  •  218
  • Sean Patrick Floyd  · 技术社区  · 15 年前

    你们知道Java映射或类似的标准数据存储在给定的超时后自动清除条目吗?这意味着老化,旧的过期条目会自动老化。

    WeakReference 基于 WeakHashMap 不是一个选项,因为我的键可能是非内部字符串,我需要一个不依赖于垃圾收集器的可配置超时。

    Ehcache 也是一个我不想依赖的选项,因为它需要外部配置文件。我正在寻找一个代码唯一的解决方案。

    10 回复  |  直到 10 年前
        1
  •  340
  •   Community Mohan Dere    8 年前

    对。谷歌收藏,或 Guava 正如它的名字现在有一个叫做 MapMaker

    ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(4)
       .softKeys()
       .weakValues()
       .maximumSize(10000)
       .expiration(10, TimeUnit.MINUTES)
       .makeComputingMap(
           new Function<Key, Graph>() {
             public Graph apply(Key key) {
               return createExpensiveGraph(key);
             }
           });
    

    更新:

    CacheBuilder

    LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
        .maximumSize(10000)
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build(
            new CacheLoader<Key, Graph>() {
              public Graph load(Key key) throws AnyException {
                return createExpensiveGraph(key);
              }
            });
    
        2
  •  32
  •   Vivek    7 年前

    这是一个示例实现,我为相同的需求和并发工作良好。可能对某人有用。

    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * 
     * @author Vivekananthan M
     *
     * @param <K>
     * @param <V>
     */
    public class WeakConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
    
        private static final long serialVersionUID = 1L;
    
        private Map<K, Long> timeMap = new ConcurrentHashMap<K, Long>();
        private long expiryInMillis = 1000;
        private static final SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss:SSS");
    
        public WeakConcurrentHashMap() {
            initialize();
        }
    
        public WeakConcurrentHashMap(long expiryInMillis) {
            this.expiryInMillis = expiryInMillis;
            initialize();
        }
    
        void initialize() {
            new CleanerThread().start();
        }
    
        @Override
        public V put(K key, V value) {
            Date date = new Date();
            timeMap.put(key, date.getTime());
            System.out.println("Inserting : " + sdf.format(date) + " : " + key + " : " + value);
            V returnVal = super.put(key, value);
            return returnVal;
        }
    
        @Override
        public void putAll(Map<? extends K, ? extends V> m) {
            for (K key : m.keySet()) {
                put(key, m.get(key));
            }
        }
    
        @Override
        public V putIfAbsent(K key, V value) {
            if (!containsKey(key))
                return put(key, value);
            else
                return get(key);
        }
    
        class CleanerThread extends Thread {
            @Override
            public void run() {
                System.out.println("Initiating Cleaner Thread..");
                while (true) {
                    cleanMap();
                    try {
                        Thread.sleep(expiryInMillis / 2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            private void cleanMap() {
                long currentTime = new Date().getTime();
                for (K key : timeMap.keySet()) {
                    if (currentTime > (timeMap.get(key) + expiryInMillis)) {
                        V value = remove(key);
                        timeMap.remove(key);
                        System.out.println("Removing : " + sdf.format(new Date()) + " : " + key + " : " + value);
                    }
                }
            }
        }
    }
    


    (带侦听器实现)

    https://github.com/vivekjustthink/WeakConcurrentHashMap

    干杯!!

        3
  •  23
  •   Guram Savinov    7 年前

    Apache Commons具有映射过期条目的装饰器: PassiveExpiringMap 它比番石榴的贮藏更简单。

    注意,它不是同步的。

        4
  •  19
  •   pcan    10 年前

    my implementation DelayQueue 每次操作都会自动清理。

        5
  •  4
  •   dan carter    14 年前

    通常,将配置移到声明性配置文件中是一个好主意(因此,当新安装需要不同的到期时间时,您不需要重新编译),但这完全不是必需的,您仍然可以通过编程方式对其进行配置。 http://www.ehcache.org/documentation/user-guide/configuration

        6
  •  3
  •   Emil    15 年前

    谷歌收藏(googlecollections)拥有 MapMaker 在其中,您可以设置时间限制(过期),并且可以在选择时使用软引用或弱引用,使用工厂方法创建所选的实例。

        7
  •  2
  •   toby941    12 年前
        8
  •  2
  •   palindrom    11 年前

    如果有人需要一个简单的东西,下面是一个简单的密钥过期集。它可以很容易地转换成地图。

    public class CacheSet<K> {
        public static final int TIME_OUT = 86400 * 1000;
    
        LinkedHashMap<K, Hit> linkedHashMap = new LinkedHashMap<K, Hit>() {
            @Override
            protected boolean removeEldestEntry(Map.Entry<K, Hit> eldest) {
                final long time = System.currentTimeMillis();
                if( time - eldest.getValue().time > TIME_OUT) {
                    Iterator<Hit> i = values().iterator();
    
                    i.next();
                    do {
                        i.remove();
                    } while( i.hasNext() && time - i.next().time > TIME_OUT );
                }
                return false;
            }
        };
    
    
        public boolean putIfNotExists(K key) {
            Hit value = linkedHashMap.get(key);
            if( value != null ) {
                return false;
            }
    
            linkedHashMap.put(key, new Hit());
            return true;
        }
    
        private static class Hit {
            final long time;
    
    
            Hit() {
                this.time = System.currentTimeMillis();
            }
        }
    }
    
        9
  •  2
  •   Matthias Ronge    10 年前

    通常,缓存应该将对象保留一段时间,并在一段时间后公开其中的一部分。是什么 取决于用例。我希望这件事是简单的,没有线程或调度程序。这种方法适合我。不像 SoftReference s、 保证对象在最短时间内可用。然而,这些记忆并没有停留在记忆中 until the sun turns into a red giant .

    例如,考虑一个响应缓慢的系统,该系统应能够检查最近是否完成了请求,在这种情况下,即使忙碌的用户多次点击按钮,也不能执行请求的操作两次。但是,如果一段时间后要求采取同样的行动,则应重新进行。

    class Cache<T> {
        long avg, count, created, max, min;
        Map<T, Long> map = new HashMap<T, Long>();
    
        /**
         * @param min   minimal time [ns] to hold an object
         * @param max   maximal time [ns] to hold an object
         */
        Cache(long min, long max) {
            created = System.nanoTime();
            this.min = min;
            this.max = max;
            avg = (min + max) / 2;
        }
    
        boolean add(T e) {
            boolean result = map.put(e, Long.valueOf(System.nanoTime())) != null;
            onAccess();
            return result;
        }
    
        boolean contains(Object o) {
            boolean result = map.containsKey(o);
            onAccess();
            return result;
        }
    
        private void onAccess() {
            count++;
            long now = System.nanoTime();
            for (Iterator<Entry<T, Long>> it = map.entrySet().iterator(); it.hasNext();) {
                long t = it.next().getValue();
                if (now > t + min && (now > t + max || now + (now - created) / count > t + avg)) {
                    it.remove();
                }
            }
        }
    }
    
        10
  •  1
  •   Anuj Dhiman    6 年前

    番石榴贮藏很容易实施。我们可以使用guava缓存在时基上过期密钥。我已经充分阅读了帖子和下面给出的我的学习重点。

    cache = CacheBuilder.newBuilder().refreshAfterWrite(2,TimeUnit.SECONDS).
                  build(new CacheLoader<String, String>(){
                    @Override
                    public String load(String arg0) throws Exception {
                        // TODO Auto-generated method stub
                        return addcache(arg0);
                    }
    
                  }
    

    参考文献: guava cache example