带有过期键的 Java 基于时间的地图/缓存

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3802370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 05:12:01  来源:igfitidea点击:

Java time-based map/cache with expiring keys

javacachingdictionary

提问by Sean Patrick Floyd

Do any of you know of a Java Map or similar standard data store that automatically purges entries after a given timeout? This means aging, where the old expired entries “age-out” automatically.

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

Preferably in an open source library that is accessible via Maven?

最好在可通过 Maven 访问的开源库中?

I know of ways to implement the functionality myself and have done it several times in the past, so I'm not asking for advice in that respect, but for pointers to a good reference implementation.

我知道自己实现功能的方法,并且过去已经做过几次,所以我不是在这方面寻求建议,而是寻求一个好的参考实现的指针。

WeakReferencebased solutions like WeakHashMapare not an option, because my keys are likely to be non-interned strings and I want a configurable timeout that's not dependent on the garbage collector.

WeakHashMap等基于WeakReference的解决方案不是一种选择,因为我的键可能是非内部字符串,并且我想要一个不依赖于垃圾收集器的可配置超时。

Ehcacheis also an option I wouldn't like to rely on because it needs external configuration files. I am looking for a code-only solution.

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

采纳答案by Shervin Asgari

Yes. Google Collections, or Guavaas it is named now has something called MapMakerwhich can do exactly that.

是的。Google Collections 或现在命名的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);
         }
       });

Update:

更新:

As of guava 10.0 (released September 28, 2011) many of these MapMaker methods have been deprecated in favour of the new CacheBuilder:

从 guava 10.0(2011 年 9 月 28 日发布)开始,许多 MapMaker 方法已被弃用,取而代之的是新的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);
          }
        });

回答by Emil

Google collections (guava) has the MapMakerin which you can set time limit(for expiration) and you can use soft or weak reference as you choose using a factory method to create instances of your choice.

Google collections (guava) 具有MapMaker,您可以在其中设置时间限制(到期),并且您可以选择使用软引用或弱引用,使用工厂方法创建您选择的实例。

回答by dan carter

Sounds like ehcache is overkill for what you want, however note that it does not need external configuration files.

听起来 ehcache 对于您想要的东西来说太过分了,但是请注意,它不需要外部配置文件。

It is generally a good idea to move configuration into a declarative configuration files ( so you don't need to recompile when a new installation requires a different expiry time ), but it is not at all required, you can still configure it programmatically. http://www.ehcache.org/documentation/user-guide/configuration

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

回答by toby941

you can try Expiring Map http://www.java2s.com/Code/Java/Collections-Data-Structure/ExpiringMap.htma class from The Apache MINA Project

您可以尝试过期地图 http://www.java2s.com/Code/Java/Collections-Data-Structure/ExpiringMap.htmApache MINA 项目中的一个类

回答by Guram Savinov

Apache Commons has decorator for Map to expire entries: PassiveExpiringMapIt's more simple than caches from Guava.

Apache Commons 有 Map 装饰器来过期条目:PassiveExpiringMap它比来自 Guava 的缓存更简单。

P.S. be careful, it's not synchronized.

PS小心,它不是同步的。

回答by pcan

You can try out my implementationof a self-expiring hash map. This implementation does not make use of threads to remove expired entries, instead it uses DelayQueuethat is cleaned up at every operation automatically.

您可以尝试的自过期哈希映射实现。此实现不使用线程来删除过期的条目,而是使用在每个操作时自动清除的DelayQueue

回答by palindrom

If anybody needs a simple thing, following is a simple key-expiring set. It might be converted to a map easily.

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

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();
        }
    }
}

回答by Anuj Dhiman

Guava cache is easy to implementation.We can expires key on time base using guava cache. I have read fully post and below gives key of my study.

Guava 缓存易于实现。我们可以使用 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);
                }

              }

Reference : guava cache example

参考:番石榴缓存示例

回答by Matthias Ronge

Typically, a cache should keep objects around some time and shall expose of them some time later. What is a good time to hold an objectdepends on the use case. I wanted this thing to be simple, no threads or schedulers. This approach works for me. Unlike SoftReferences, objects are guaranteed to be available some minimum amount of time. However, the do not stay around in memory until the sun turns into a red giant.

通常,缓存应该将对象保留一段时间,并在一段时间后公开它们。什么是持有对象的好时机取决于用例。我希望这件事很简单,没有线程或调度程序。这种方法对我有用。与SoftReferences不同,对象可以保证在最短的时间内可用。然而,在太阳变成红巨星之前不要留在记忆中。

As useage example think of a slowly responding system that shall be able to check if a request has been done quite recently, and in that case not to perform the requested action twice, even if a hectic user hits the button several times. But, if the same action is requested some time later, it shall be performed again.

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

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();
            }
        }
    }
}

回答by Vivek

This is a sample implementation that i did for the same requirement and concurrency works well. Might be useful for someone.

这是我为相同的要求所做的示例实现,并发运行良好。可能对某人有用。

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);
                }
            }
        }
    }
}


Git Repo Link(With Listener Implementation)


Git Repo 链接(带监听器实现)

https://github.com/vivekjustthink/WeakConcurrentHashMap

https://github.com/vivekjustthink/WeakConcurrentHashMap

Cheers!!

干杯!!