मैं नए ComputeIfAbsent फ़ंक्शन का उपयोग कैसे करूं?


115

मैं बहुत ज्यादा Map.computeIfAbsent का उपयोग करना चाहता हूं, लेकिन अंडरग्राउंड में लंबोदर के बाद से यह बहुत लंबा है।

डॉक्स से लगभग सीधे: यह चीजों को करने के पुराने तरीके का एक उदाहरण देता है:

Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>();
String key = "snoop";
if (whoLetDogsOut.get(key) == null) {
  Boolean isLetOut = tryToLetOut(key);
  if (isLetOut != null)
    map.putIfAbsent(key, isLetOut);
}

और नया तरीका:

map.computeIfAbsent(key, k -> new Value(f(k)));

लेकिन उनके उदाहरण में, मुझे लगता है कि मैं काफी "ऐसा नहीं हूं।" मैं इसे व्यक्त करने के नए लैम्ब्डा तरीके का उपयोग करने के लिए कोड को कैसे बदलूंगा?


मुझे यकीन नहीं है कि आप वहां के उदाहरण से क्या समझते हैं?
लुई वासरमैन

2
"के" क्या है? क्या यह एक चर परिभाषित किया जा रहा है? कैसे "नए मूल्य" के बारे में - यह है कि जावा 8 से कुछ, या एक वस्तु का प्रतिनिधित्व करने के लिए मुझे परिभाषित या ओवरराइड करने की आवश्यकता है? whoLetDogsOut.computeIfAbsent (key, k -> new Boolean (tryToLetOut (k))) संकलन नहीं करता है, इसलिए मुझे कुछ याद आ रहा है ...
बेंजामिन एच।

क्या वास्तव में संकलन नहीं है? यह क्या त्रुटि पैदा करता है?
axtavt

Temp.java:26: त्रुटि: अभिव्यक्ति की अवैध शुरुआत जोLetDogsOut.computeIfAbsent (कुंजी, k -> नया बूलियन (tryToLetOut (k)))); (">" की ओर इशारा करते हुए)
बेंजामिन एच

मेरे लिए ठीक संकलन है। सुनिश्चित करें कि आप वास्तव में जावा 8 कंपाइलर का उपयोग करते हैं। क्या अन्य जावा 8 सुविधाएँ काम करती हैं?
axtavt

जवाबों:


97

मान लें कि आपके पास निम्नलिखित कोड है:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Test {
    public static void main(String[] s) {
        Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>();
        whoLetDogsOut.computeIfAbsent("snoop", k -> f(k));
        whoLetDogsOut.computeIfAbsent("snoop", k -> f(k));
    }
    static boolean f(String s) {
        System.out.println("creating a value for \""+s+'"');
        return s.isEmpty();
    }
}

तब आपको संदेश creating a value for "snoop"एक बार ठीक उसी प्रकार दिखाई देगा, जैसा कि दूसरे आह्वान पर computeIfAbsentउस कुंजी के लिए पहले से ही एक मूल्य है। kलैम्ब्डा अभिव्यक्ति में k -> f(k)बस कुंजी जो नक्शे मूल्य की गणना के लिए अपने लैम्ब्डा में पास के लिए एक placeolder (पैरामीटर) है। इसलिए उदाहरण में कुंजी को फ़ंक्शन मंगलाचरण में पास किया जाता है।

वैकल्पिक रूप से आप लिख सकते हैं: whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty());सहायक परिणाम के बिना एक ही परिणाम प्राप्त करने के लिए (लेकिन आप तब डिबगिंग आउटपुट नहीं देखेंगे)। और इससे भी सरल, क्योंकि यह एक मौजूदा विधि के लिए एक साधारण प्रतिनिधिमंडल है जिसे आप लिख सकते हैं: whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty);इस प्रतिनिधिमंडल को लिखे जाने के लिए किसी भी पैरामीटर की आवश्यकता नहीं है।

अपने प्रश्न में उदाहरण के करीब होने के लिए, आप इसे लिख सकते हैं whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));(यह कोई फर्क नहीं पड़ता कि आप पैरामीटर नाम देते हैं kया नहीं key)। या यह लिखने के रूप में whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);करता है, तो tryToLetOutहै staticया whoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);अगर tryToLetOutएक उदाहरण विधि है।


114

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

हम मानचित्र को परिभाषित करके और आधार मामलों के लिए उसमें मूल्य डालकर, fibonnaci(0)और fibonacci(1):

private static Map<Integer,Long> memo = new HashMap<>();
static {
   memo.put(0,0L); //fibonacci(0)
   memo.put(1,1L); //fibonacci(1)
}

और हमें जो कुछ भी करना है उसके लिए आगमनात्मक कदम के लिए हमारे फिबोनाची समारोह को फिर से परिभाषित करना है:

public static long fibonacci(int x) {
   return memo.computeIfAbsent(x, n -> fibonacci(n-2) + fibonacci(n-1));
}

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


18
गतिशील प्रोग्रामिंग के लिए अच्छा, एकल-पंक्ति रूपांतरण। बहुत चालाक है।
बेंजामिन एच

3
यदि आपके पास पहले (n-2) कॉल है तो आपको कम पुनरावर्ती कॉल मिल सकती हैं?
थोरबजोरन राव एंडरसन

9
जब आप computeIfAbsent का पुनरावर्ती उपयोग करते हैं तो आपको अधिक सतर्क होना चाहिए। अधिक जानकारी के लिए कृपया stackoverflow.com/questions/28840047/…
अजीत कुमार

11
इस कोड के परिणाम HashMapभ्रष्ट हो रहे हैं, जैसे कि Bugs.openjdk.java.net/browse/JDK-8172951 में और ConcurrentModificationExceptionजावा 9 में विफल रहेगा ( Bugs.openjdk.java.net/browse/JDK-8071667 )
Piotr

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

41

एक और उदाहरण। मानचित्रों के एक जटिल मानचित्र का निर्माण करते समय, computeIfAbsent () विधि मानचित्र के प्राप्त () विधि के लिए एक प्रतिस्थापन है। ComputeIfAbsent () कॉल को एक साथ जोड़ने के माध्यम से, लापता कंटेनरों को प्रदान किए गए लंबोदर द्वारा बनाया जाता है:

  // Stores regional movie ratings
  Map<String, Map<Integer, Set<String>>> regionalMovieRatings = new TreeMap<>();

  // This will throw NullPointerException!
  regionalMovieRatings.get("New York").get(5).add("Boyhood");

  // This will work
  regionalMovieRatings
    .computeIfAbsent("New York", region -> new TreeMap<>())
    .computeIfAbsent(5, rating -> new TreeSet<>())
    .add("Boyhood");

31

बहु नक्शा

यह वास्तव में उपयोगी है यदि आप इसके कार्यान्वयन के लिए Google अमरूद पुस्तकालय का सहारा लिए बिना एक मल्टीमैप बनाना चाहते हैं ।MultiMap

उदाहरण के लिए, मान लीजिए कि आप उन छात्रों की सूची संग्रहीत करना चाहते हैं, जिन्होंने किसी विशेष विषय के लिए दाखिला लिया है।

JDK लाइब्रेरी का उपयोग करने के लिए इसका सामान्य समाधान है:

Map<String,List<String>> studentListSubjectWise = new TreeMap<>();
List<String>lis = studentListSubjectWise.get("a");
if(lis == null) {
    lis = new ArrayList<>();
}
lis.add("John");

//continue....

चूंकि इसमें कुछ बॉयलरप्लेट कोड है, इसलिए लोग अमरूद का उपयोग करते हैं Mutltimap

Map.computeIfAbsent का उपयोग करके, हम अमरूद मल्टीमप के बिना एक पंक्ति में निम्नानुसार लिख सकते हैं।

studentListSubjectWise.computeIfAbsent("a", (x -> new ArrayList<>())).add("John");

स्टुअर्ट मार्क्स और ब्रायन गोएत्ज़ ने इस https://www.youtube.com/watch?v=9uTVXxJucuco के बारे में एक अच्छी बात की


जावा 8 (और अधिक संक्षिप्त) में मल्टीमैप बनाने का एक और तरीका यह है कि यह जेडडीके में केवल studentListSubjectWise.stream().collect(Collectors.GroupingBy(subj::getSubjName, Collectors.toList());एक प्रकार का मल्टी-मैप तैयार करता है और अधिक संक्षिप्त Map<T,List<T>रूप से imho है।
लाश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.