जावा समय-आधारित मानचित्र / कैशिंग समाप्ति कुंजी के साथ [बंद]


253

क्या आपमें से किसी को जावा मैप या इसी तरह के मानक डेटा स्टोर के बारे में पता है जो किसी दिए गए टाइमआउट के बाद स्वचालित रूप से प्रविष्टियों को शुद्ध करता है? इसका मतलब उम्र बढ़ने, जहां पुरानी समाप्त हो चुकी प्रविष्टियाँ "आयु-आउट" स्वचालित रूप से होती हैं।

अधिमानतः एक खुले स्रोत पुस्तकालय में जो मावेन के माध्यम से सुलभ है?

मैं स्वयं कार्यक्षमता को लागू करने के तरीकों के बारे में जानता हूं और अतीत में कई बार कर चुका हूं, इसलिए मैं उस संबंध में सलाह नहीं मांग रहा हूं, लेकिन एक अच्छे संदर्भ कार्यान्वयन के लिए संकेत के लिए।

WeakHashMap की तरह WeakReference आधारित समाधान एक विकल्प नहीं है, क्योंकि मेरी चाबियाँ गैर-आंतरिक स्ट्रिंग्स होने की संभावना है और मैं एक कॉन्फ़िगर करने योग्य समयबाह्य चाहता हूं जो कचरा कलेक्टर पर निर्भर नहीं है।

Ehcache भी एक विकल्प है जिस पर मैं भरोसा नहीं करना चाहूंगा क्योंकि इसे बाहरी कॉन्फ़िगरेशन फ़ाइलों की आवश्यकता है। मैं एक कोड-ओनली सॉल्यूशन की तलाश में हूं।


1
Google संग्रह (जिसे अब अमरूद कहा जाता है) देखें। इसमें एक नक्शा है जो प्रविष्टियों को स्वचालित रूप से टाइमआउट कर सकता है।
dty

3
यह कितना अजीब है कि 253 upvotes और 176k विचारों के साथ एक प्रश्न - जो इस विषय के लिए खोज इंजन में उच्च स्थान पर है - दिशा
ब्रायन

जवाबों:


320

हाँ। Google संग्रह या अमरूद के नाम के साथ अब कुछ ऐसा है जिसे 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);
         }
       });

अपडेट करें:

के रूप में अमरूद 10.0 (28 सितंबर, 2011 को जारी किया गया) इनमें से कई मैपमेकर तरीकों को नए कैशेबर्ल के पक्ष में चित्रित किया गया है :

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

5
बहुत बढ़िया, मुझे पता था कि अमरूद का जवाब है, लेकिन मैं इसे नहीं पा सका! (+1)
सीन पैट्रिक फ़्लॉइड

12
V10 से रूप में, आप CacheBuilder बजाय (का उपयोग करना चाहिए guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/... समाप्ति आदि MapMaker में अमान्य हो जाने के बाद से)
wwadge

49
चेतावनी ! का उपयोग कर weakKeys()मतलब है कि कुंजी == अर्थ विज्ञान का उपयोग कर, नहीं की तुलना की जाती equals()। मैंने 30 मिनट का समय निकालकर सोचा कि मेरा स्ट्रिंग-कुंजी वाला कैश काम क्यों नहीं कर रहा है :)
लॉरेंट ग्रैगोइरे

3
दोस्तों, बात है कि @Laurent के बारे weakKeys()में उल्लेख किया है महत्वपूर्ण है। weakKeys()90% समय की आवश्यकता नहीं है।
मनु मंजूनाथ

3
शुरुआती लोगों के लिए @ShervinAsgari (खुद शामिल), क्या आप अपने अपडेट किए गए अमरूद के उदाहरण को स्विच कर सकते हैं जो LoadingCache के बजाय Cache का उपयोग करता है? यह प्रश्न को बेहतर तरीके से मिलाएगा (चूँकि LoadingCache में ऐसी विशेषताएँ हैं जो प्रविष्टियों को समाप्त करने वाले नक्शे से अधिक हैं और इसे बनाने के लिए अधिक जटिल है) github.com/google/guava/wiki/CachesExplained#from-a-callable
Jutnarg

29

यह एक नमूना कार्यान्वयन है जो मैंने उसी आवश्यकता के लिए किया था और समरूपता अच्छी तरह से काम करती है। किसी के लिए उपयोगी हो सकता है।

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 रेपो लिंक (श्रोता कार्यान्वयन के साथ)

https://github.com/vivekjustthink/WeakConcurrentHashMap

चीयर्स !!


आप cleanMap()आधे से अधिक समय का निष्पादन क्यों करते हैं ?
एलयूएक्सएक्स

Bcoz यह सुनिश्चित करता है कि चाबियाँ समाप्त हो गई हैं (हटा दी गई हैं) और चरम लूपिंग से धागे से बचा जाता है।
विवेक

@ विवेक लेकिन इस कार्यान्वयन के साथ प्रविष्टियों की अधिकतम (एक्सपायरी इनमिलिस / 2) संख्या हो सकती है जो पहले ही समाप्त हो चुकी हैं लेकिन अभी भी कैश में मौजूद हैं। जैसा कि धागा समाप्ति के बाद प्रविष्टियों को हटाता हैमिली / 2 अवधि
rishi007bansod

19

आप एक आत्म-निष्कासन हैश मानचित्र के मेरे कार्यान्वयन की कोशिश कर सकते हैं । यह कार्यान्वयन समय-समय पर प्रविष्टियों को हटाने के लिए थ्रेड्स का उपयोग नहीं करता है, इसके बजाय यह DelayQueue का उपयोग करता है जो हर ऑपरेशन में स्वचालित रूप से साफ हो जाता है।


मुझे अमरूद का संस्करण बेहतर लगता है, लेकिन चित्र में पूर्णता जोड़ने के लिए +1
सीन पैट्रिक फ्लॉयड

@ piero86 मैं कहता हूं कि विधि expireKey (ExpiringKey <K> delayedKey) में delayQueue.poll () कॉल गलत है। आप एक मनमाने ढंग से एक्सपायरिंगकेय को ढीला कर सकते हैं जिसे बाद में क्लीनअप () में उपयोग नहीं किया जा सकता है।
स्टीफन जोबल

1
एक और समस्या: आप अलग-अलग जीवनकाल के साथ एक ही कुंजी को दो बार नहीं डाल सकते हैं। A) put (1, 1, shortLived) के बाद, b) put (1, 2, longLived) की कुंजी 1 के लिए मैप प्रविष्टि, shortLived ms के बाद चली जाएगी, चाहे कितना भी लंबा हो।
स्टीफन ज़ोबेल

आपके निरीक्षण के लिए धन्यवाद। क्या आप इन मुद्दों को टिप्पणी के रूप में रिपोर्ट कर सकते हैं, कृपया?
पान

आपके सुझावों के अनुसार निश्चित। धन्यवाद।
पेंक

19

Apache Commons में मानचित्र के लिए प्रविष्टियां समाप्त करने के लिए डेकोरेटर है: PassiveExpiringMap यह अमरूद से कैश की तुलना में अधिक सरल है।

पुनश्च सावधान रहें, यह सिंक्रनाइज़ नहीं है।


1
यह सरल है, लेकिन आपके द्वारा किसी प्रविष्टि तक पहुंचने के बाद ही यह समाप्ति समय की जांच करता है।
बादी

Javadoc के अनुसार : जब इनवोक करने के तरीके जिसमें पूरे मैप कंटेंट (यानी, कंकी (ऑब्जेक्ट), एंट्रीसेट (), इत्यादि) को शामिल किया जाता है, तो यह डेकोरेटर वास्तव में इनवोकेशन को पूरा करने से पहले सभी एक्सपायर्ड एंट्री को हटा देता है।
एनएस डू टिट

यदि आप यह देखना चाहते हैं कि इस लाइब्रेरी का नवीनतम संस्करण (Apache commons-collection4) यहाँ mvanrepository पर संबंधित लाइब्रेरी का लिंक क्या है
NS du Toit

3

इहचे जैसी ध्वनियाँ जो आप चाहते हैं, उसके लिए ओवरकिल है, हालांकि ध्यान दें कि इसे बाहरी कॉन्फ़िगरेशन फ़ाइलों की आवश्यकता नहीं है।

आमतौर पर कॉन्फ़िगरेशन को डिक्लेक्टिव कॉन्फ़िगरेशन फ़ाइलों में ले जाना एक अच्छा विचार है (इसलिए जब आपको किसी नए इंस्टॉलेशन के लिए अलग एक्सपायरी समय की आवश्यकता होती है, तो उसे पुन: कनेक्ट करने की आवश्यकता नहीं है), लेकिन यह आवश्यक नहीं है, फिर भी आप इसे प्रोग्रामेटिक रूप से कॉन्फ़िगर कर सकते हैं। http://www.ehcache.org/documentation/user-guide/configuration


2

Google संग्रह (अमरूद) में मैपमेकर होता है जिसमें आप समय सीमा (समाप्ति के लिए) निर्धारित कर सकते हैं और अपनी पसंद के उदाहरण बनाने के लिए फ़ैक्टरी विधि का उपयोग करते हुए आप नरम या कमजोर संदर्भ का उपयोग कर सकते हैं।



2

यदि किसी को एक साधारण चीज की जरूरत है, तो निम्नलिखित एक सरल कुंजी-समाप्ति सेट है। इसे आसानी से मानचित्र में बदला जा सकता है।

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

2
यह सिंगल-थ्रेड स्थिति के लिए ठीक है, लेकिन यह समवर्ती स्थिति में बुरी तरह से टूट जाएगा।
सीन पैट्रिक फ्लोयड

@SeanPatrickFloyd आप लिंक्डहैश के खुद की तरह मतलब है ?! LinkedHashMap, HashMap की तरह "इसे बाहरी रूप से सिंक्रनाइज़ किया जाना चाहिए" ... आप इसे नाम देते हैं।
पालिंड्रोम

हाँ, उन सभी की तरह, लेकिन अमरूद के कैश (स्वीकृत उत्तर) के विपरीत
सीन पैट्रिक फ्लोयड

इसके अलावा, System.nanoTime()समय के अंतर के लिए कंप्यूटिंग का उपयोग करने पर विचार करें क्योंकि System.currentTimeMillis () सुसंगत नहीं है क्योंकि यह सिस्टम के समय पर निर्भर करता है और निरंतर नहीं हो सकता है।
Ercksen

2

आमतौर पर, एक कैश को कुछ समय के आसपास वस्तुओं को रखना चाहिए और कुछ समय बाद उन्हें बेनकाब करना चाहिए। किसी वस्तु को रखने का एक अच्छा समय क्या है, यह उपयोग के मामले पर निर्भर करता है। मैं चाहता था कि यह बात सरल हो, कोई सूत्र या अनुसूचक न हो। यह दृष्टिकोण मेरे लिए काम करता है। SoftReferenceएस के विपरीत , वस्तुओं को कुछ न्यूनतम समय उपलब्ध होने की गारंटी दी जाती है। हालांकि, जब तक सूरज एक लाल विशालकाय में बदल नहीं जाता है तब तक स्मृति में न रहें

जैसा कि उदाहरण उदाहरण धीरे-धीरे प्रतिक्रिया देने वाले सिस्टम के बारे में सोचते हैं जो यह जांचने में सक्षम होगा कि क्या एक अनुरोध हाल ही में किया गया है, और उस मामले में दो बार अनुरोधित कार्रवाई नहीं की जाती है, भले ही एक व्यस्त उपयोगकर्ता कई बार बटन दबाए। लेकिन, अगर कुछ समय बाद उसी कार्रवाई का अनुरोध किया जाता है, तो इसे फिर से किया जाएगा।

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

अच्छा, धन्यवाद
bigbadmouse

1
दौड़ की स्थितियों, map.put संचालन या मानचित्र के आकार बदलने के कारण HashMap सुरक्षित नहीं है, इससे डेटा भ्रष्टाचार हो सकता है। यहां देखें: mailinator.blogspot.com/2009/06/beautiful-race-condition.html
यूजीन मेयसुक

यह सच है। वास्तव में, अधिकांश जावा कक्षाएं थ्रेड सुरक्षित नहीं हैं। यदि आपको थ्रेड सुरक्षा की आवश्यकता है, तो आपको यह देखने के लिए अपने डिजाइन के प्रत्येक प्रभावित वर्ग की जांच करने की आवश्यकता है कि क्या यह आवश्यकता को पूरा करता है।
मथायस रिज

1

अमरूद कैश को लागू करना आसान है। हम अमरूद कैश का उपयोग करके समय के आधार पर कुंजी को समाप्त कर सकते हैं। मैंने पूरी तरह से पोस्ट पढ़ी है और नीचे मेरे अध्ययन की कुंजी है।

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

              }

संदर्भ: अमरूद कैश उदाहरण


1
कृपया लिंक अपडेट करें क्योंकि यह अभी काम नहीं कर रहा है
smaiakov
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.