2016-02-11 5 views
17

अमरूद पुस्तकालय का अपना Supplier है जो जावा 8 Supplier का विस्तार नहीं करता है। इसके अलावा अमरूद आपूर्तिकर्ताओं के लिए एक कैश प्रदान करता है - Suppliers#memoizeक्या जावा 8 ने आपूर्तिकर्ताओं के लिए कैश समर्थन किया है?

क्या कुछ समान है, लेकिन जावा 8 प्रदायक के लिए?

+8

बिल्कुल नहीं, लेकिन आप अंत में ':: get' लिखकर j.u.f.Suppliers और c.g.c.b.Suppliers के बीच आसानी से परिवर्तित कर सकते हैं। –

+2

@LouisWasserman के रूप में सुझाव देता है, आप मूल रूप से "वापसी Suppliers.memoize (प्रतिनिधि :: प्राप्त) :: प्राप्त करने के द्वारा guava Suppliers :: memoize के लिए एक रैपर बना सकते हैं;" – jvdneste

+1

यह निश्चित रूप से एक दयालुता है कि Suppliers.memoize ने इसे jdk8 मानक लाइब्रेरी में नहीं बनाया है, क्योंकि यह मेरे लिए बहुत कम जोखिम वाले जोखिम की तरह लगता है। – jvdneste

उत्तर

16

कोई Memoization के लिए निर्मित है जावा समारोह है, हालांकि यह बहुत मुश्किल, इसे लागू करने के इस तरह उदाहरण के लिए, नहीं है:

public static <T> Supplier<T> memoize(Supplier<T> delegate) { 
    AtomicReference<T> value = new AtomicReference<>(); 
    return() -> { 
     T val = value.get(); 
     if (val == null) { 
      val = value.updateAndGet(cur -> cur == null ? 
        Objects.requireNonNull(delegate.get()) : cur); 
     } 
     return val; 
    }; 
} 

ध्यान दें कि विभिन्न कार्यान्वयन दृष्टिकोण मौजूद हैं। उपर्युक्त कार्यान्वयन प्रतिनिधि को कई बार कॉल कर सकता है यदि ज्ञापन सप्लायर विभिन्न धागे से कई बार अनुरोध करता है। कभी-कभी लॉक के साथ स्पष्ट सिंक्रनाइज़ेशन पर ऐसे कार्यान्वयन को प्राथमिकता दी जाती है। ताला पसंद किया जाता है, तो DCL इस्तेमाल किया जा सकता है:

public static <T> Supplier<T> memoizeLock(Supplier<T> delegate) { 
    AtomicReference<T> value = new AtomicReference<>(); 
    return() -> { 
     T val = value.get(); 
     if (val == null) { 
      synchronized(value) { 
       val = value.get(); 
       if (val == null) { 
        val = Objects.requireNonNull(delegate.get()); 
        value.set(val); 
       } 
      } 
     } 
     return val; 
    }; 
} 

यह भी ध्यान रखें, के रूप में @LouisWasserman सही ढंग से टिप्पणी में उल्लेख किया है, तो आप आसानी से अमरूद आपूर्तिकर्ता में JDK आपूर्तिकर्ता बदल सकता है और इसके विपरीत विधि संदर्भ का उपयोग करते हुए:

java.util.function.Supplier<String> jdkSupplier =() -> "test"; 
com.google.common.base.Supplier<String> guavaSupplier = jdkSupplier::get; 
java.util.function.Supplier<String> jdkSupplierBack = guavaSupplier::get; 

तो गुवा और जेडीके कार्यों के बीच स्विच करने की कोई बड़ी समस्या नहीं है।

+1

आपको इस मामले में वास्तव में 'परमाणु संदर्भ' की आवश्यकता नहीं है, क्या आप? ऐसा लगता है कि एक म्यूटेबल कंटेनर के रूप में प्रयोग किया जाता है कि लैम्ब्डा बंद हो सकता है। यदि आप एक ऑब्जेक्ट आवंटन को सहेजना चाहते हैं तो मुझे लगता है कि आप एक अज्ञात वर्ग मान को अस्थिर 'मान' फ़ील्ड के साथ वापस कर सकते हैं। 'इस' पर एक सिंक्रनाइज़ करें। – Lii

+1

@Lii, 'AtomicReference' केवल एक ऐसा क्षेत्र वाला एक ऑब्जेक्ट है जो अस्थिर पढ़ने/लिखने वाला अर्थपूर्ण प्रदान करता है जो यहां आवश्यक है। अज्ञात वर्ग अस्थिर क्षेत्र (केवल दूसरे नमूने में, पहले में नहीं) के साथ इसे प्रतिस्थापित करना संभव है, लेकिन यह स्पष्ट नहीं है कि इस तरह के अनुकूलन महत्वपूर्ण हैं या नहीं।इसके अलावा, सार्वजनिक रूप से उपलब्ध वस्तु पर लॉक करना एक बुरा अभ्यास माना जाता है। –

+1

आप '() -> val' के किसी अन्य आपूर्तिकर्ता को याद करके अस्थिर अर्थशास्त्र को खत्म कर सकते हैं। इस तरह, आप कब्जे वाले मूल्य के 'अंतिम' फ़ील्ड अर्थशास्त्र का उपयोग कर रहे हैं। – Holger

18

सरल समाधान

public static <T> Supplier<T> memoize(Supplier<T> original) { 
    ConcurrentHashMap<Object, T> store=new ConcurrentHashMap<>(); 
    return()->store.computeIfAbsent("dummy", key->original.get()); 
} 

होगा हालांकि, सबसे सरल हमेशा सबसे कारगर नहीं है।

आप एक स्वच्छ और कुशल समाधान चाहते हैं, एक गुमनाम आंतरिक वर्ग का सहारा धारण करने के लिए परिवर्तनशील राज्य बंद का भुगतान करेगा:

public static <T> Supplier<T> memoize1(Supplier<T> original) { 
    return new Supplier<T>() { 
     Supplier<T> delegate = this::firstTime; 
     boolean initialized; 
     public T get() { 
      return delegate.get(); 
     } 
     private synchronized T firstTime() { 
      if(!initialized) { 
       T value=original.get(); 
       delegate=() -> value; 
       initialized=true; 
      } 
      return delegate.get(); 
     } 
    }; 
} 

यह एक प्रतिनिधि आपूर्तिकर्ता जो पहली बार आपरेशन करते हैं और बाद में होगा उपयोग करता है, अपने आप को एक आपूर्तिकर्ता के साथ प्रतिस्थापित करें जो पहले मूल्यांकन के कब्जे वाले परिणाम को बिना शर्त रूप से लौटाता है। चूंकि इसमें final फ़ील्ड सेमेन्टिक्स हैं, इसलिए बिना किसी अतिरिक्त सिंक्रनाइज़ेशन के इसे बिना शर्त रूप से वापस किया जा सकता है।

synchronized विधि firstTime() के अंदर, वहाँ अभी भी एक initialized ध्वज की जरूरत है क्योंकि प्रारंभ दौरान समवर्ती पहुंच के मामले में, एक से अधिक थ्रेड विधि के प्रवेश पर प्रतीक्षा कर सकते हैं इससे पहले कि प्रतिनिधि प्रतिस्थापित किया गया है है। इसलिए, इन धागे को यह पता लगाने की आवश्यकता है कि प्रारंभिकता पहले ही हो चुकी है। बाद के सभी एक्सेस नए प्रतिनिधि सप्लायर को पढ़ेंगे और मूल्य जल्दी प्राप्त करेंगे।

+1

बहुत ही रोचक जवाब। क्या आप समझा सकते हैं कि सिंक्रनाइज़ेशन या 'अस्थिर' संशोधक के बिना सीधे 'delegate.get() 'को वापस कैसे थ्रेड-सुरक्षित है? यह कैसे गारंटी दी जाती है कि वहां पहुंचने वाले सभी थ्रेड अपडेट किए गए प्रतिनिधि को 'get'' कहते हैं? – glts

+1

@glts: 'delegate.get()' पहले सिंक्रनाइज़ेशन विधि के पहले 'सिंक्रनाइज़' विधि 'firstTime()' पर या '() -> value' lambda अभिव्यक्ति से जुड़े उदाहरण पर, समाप्त हो जाएगा , जबकि 'मूल्य' प्रभावी रूप से अंतिम है। उस कैप्चर किए गए मान तक पहुंच एक 'अंतिम' फ़ील्ड पढ़ने के बराबर है जो अतिरिक्त सिंक्रनाइज़ेशन के बिना सुरक्षित है। यदि थ्रेड 'प्रतिनिधि' संदर्भ के लिए एक पुराना मूल्य देखता है, तो यह एक ही आमंत्रण के लिए 'सिंक्रनाइज़' 'firstTime()' विधि के माध्यम से जाएगा और बाद में अद्यतित मूल्य को पता चलेगा, इसलिए सभी बाद के आमंत्रण तब तेज़ पथ। – Holger

+2

उस मामले में 'प्रतिनिधि' को 'अस्थिर 'चिह्नित करने की आवश्यकता क्यों नहीं है? –

संबंधित मुद्दे