मैं हैशमैप के हैशमैप को दोहराने के कोड को दोहराने से कैसे बच सकता हूं?


27

प्रत्येक ग्राहक के पास दिनांक के साथ, इनवॉइस का एक आईडी और कई चालान होते हैं, जिन्हें आईडी द्वारा ग्राहकों के हैशमैप के रूप में संग्रहीत किया जाता है, तिथि के आधार पर चालान का

HashMap<LocalDateTime, Invoice> allInvoices = allInvoicesAllClients.get(id);

if(allInvoices!=null){
    allInvoices.put(date, invoice);      //<---REPEATED CODE
}else{
    allInvoices = new HashMap<>();
    allInvoices.put(date, invoice);      //<---REPEATED CODE
    allInvoicesAllClients.put(id, allInvoices);
}

जावा समाधान का उपयोग करने के लिए लगता है getOrDefault:

HashMap<LocalDateTime, Invoice> allInvoices = allInvoicesAllClients.getOrDefault(
    id,
    new HashMap<LocalDateTime, Invoice> (){{  put(date, invoice); }}
);

लेकिन अगर यह शून्य नहीं है, तो मैं अभी भी निष्पादित करने के लिए पुट (तारीख, चालान) चाहता हूं, और "allInvoicesAllClients" में डेटा जोड़ना अभी भी आवश्यक है। तो यह ज्यादा मदद करने के लिए प्रतीत नहीं होता है।


यदि आप कुंजी की विशिष्टता की गारंटी नहीं दे सकते हैं, तो आपके लिए सबसे अच्छा शर्त यह है कि माध्यमिक मानचित्र में केवल चालान के बजाय सूची <चालान> का मूल्य है।
रयान

जवाबों:


39

यह एक उत्कृष्ट उपयोग के लिए मामला है Map#computeIfAbsent। आपका स्निपेट अनिवार्य रूप से इसके बराबर है:

allInvoicesAllClients.computeIfAbsent(id, key -> new HashMap<>()).put(date, invoice);

यदि इसमें idएक कुंजी के रूप में मौजूद नहीं है allInvoicesAllClients, तो यह idएक नए से मानचित्रण बनाएगा HashMapऔर नया लौटाएगा HashMap। यदि idएक कुंजी के रूप में मौजूद है, तो यह मौजूदा वापस कर देगा HashMap


1
computeIfAbsent, एक get (id) (या get (id) द्वारा लगाया गया पुट) करता है, इसलिए अगली पुट को सही पुट (तारीख), सही उत्तर के लिए किया जाता है।
हर्नान एचे

allInvoicesAllClients.computeIfAbsent(id, key -> Map.of(date, invoice))
अलेक्जेंडर -

1
@ अलेक्जेंडर-रिनस्टोमोनिका Map.ofएक अविश्वसनीय बनाता है Map, जो मुझे यकीन नहीं है कि ओपी चाहता है।
जैकब जी

क्या ओपी के पास मूल रूप से यह कोड कम कुशल होगा? यह पूछने पर क्योंकि मैं इस बात से परिचित नहीं हूं कि जावा लैम्डा कार्यों को कैसे संभालता है।
जेकॉन्ग हू

16

computeIfAbsentइस विशेष मामले के लिए एक महान समाधान है। सामान्य तौर पर, मैं निम्नलिखित पर ध्यान देना चाहता हूं, क्योंकि किसी ने अभी तक इसका उल्लेख नहीं किया है:

"बाहरी" हैशमैप सिर्फ "इनर" हैशमैप के लिए एक संदर्भ संग्रहीत करता है , इसलिए आप कोड दोहराव से बचने के लिए बस संचालन को फिर से कर सकते हैं:

HashMap<LocalDateTime, Invoice> allInvoices = allInvoicesAllClients.get(id);

if (allInvoices == null) {           
    allInvoices = new HashMap<>();
    allInvoicesAllClients.put(id, allInvoices);
}

allInvoices.put(date, invoice);      // <--- no longer repeated

इस तरह से हमने दशकों पहले जावा 8 को अपने फैंसी computeIfAbsent()पद्धति के साथ आने के लिए ऐसा किया !
नील बार्लेट

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

11

आपको बहुत ज्यादा कभी भी "डबल ब्रेस" मैप इनिशियलाइज़ेशन का उपयोग नहीं करना चाहिए।

{{  put(date, invoice); }}

इस मामले में, आपको उपयोग करना चाहिए computeIfAbsent

allInvoicesAllClients.computeIfAbsent(id, (k) -> new HashMap<>())
                     .put(date, allInvoices);

यदि इस आईडी का कोई मानचित्र नहीं है, तो आप एक प्रविष्ट करेंगे। परिणाम मौजूदा या गणना मानचित्र होगा। फिर आप putगारंटी के साथ उस नक्शे में आइटम कर सकते हैं कि यह शून्य नहीं होगा।


1
मुझे नहीं पता कि कौन नीचे है, मुझे नहीं, शायद सिंगल लाइन कोड सभी को भ्रमित कर रहा है। डालिए, क्योंकि आप तारीख के बजाय आईडी का उपयोग करते हैं, मैं इसे संपादित करूंगा
हर्नान एचे

1
@ हर्नानशेई आह। मेरी गलती। धन्यवाद। हाँ के लिए idभी किया जाता है। आप computeIfAbsentएक सशर्त के बारे में सोच सकते हैं यदि आप चाहें। और यह मान भी लौटाता है
माइकल

" आपको बहुत अधिक कभी भी" डबल ब्रेस "मानचित्र आरंभीकरण का उपयोग नहीं करना चाहिए। " क्यों? (मुझे संदेह नहीं है कि आप सही हैं; मैं वास्तविक जिज्ञासा से बाहर पूछ रहा हूं।)
हेनजी

1
@ हेंज़ी क्योंकि यह एक अनाम आंतरिक वर्ग बनाता है। यह उस वर्ग के लिए एक संदर्भ रखता है जिसने इसे घोषित किया था, जो यदि आप मानचित्र को उजागर करते हैं (उदाहरण के लिए एक गेटर के माध्यम से) तो संलग्नक वर्ग को कचरा एकत्र होने से रोक देगा। इसके अलावा, मुझे लगता है कि यह जावा से कम परिचित लोगों के लिए भ्रामक हो सकता है; शुरुआती ब्लॉक शायद ही कभी इस्तेमाल किए जाते हैं, और इसे इस तरह से लिखना ऐसा लगता है जैसे {{ }}इसका विशेष अर्थ है, जो यह नहीं करता है।
माइकल

1
@ मिचेल: समझदारी, धन्यवाद। मैं पूरी तरह से भूल गया कि अनाम आंतरिक कक्षाएं हमेशा गैर-स्थिर होती हैं (भले ही उन्हें होने की आवश्यकता न हो)।
हेनजी

5

यह अन्य उत्तरों की तुलना में लंबा है, लेकिन इससे अधिक पठनीय है:

if(!allInvoicesAllClients.containsKey(id))
    allInvoicesAllClients.put(id, new HashMap<LocalDateTime, Invoice>());

allInvoicesAllClients.get(id).put(date, invoice);

3
यह एक HashMap के लिए काम कर सकता है लेकिन सामान्य दृष्टिकोण इष्टतम नहीं है। यदि ये समवर्ती हैं, तो ये ऑपरेशन परमाणु नहीं हैं। इस तरह के मामले में, चेक-एक्ट अधिनियम में दौड़ की स्थिति पैदा करेगा। वैसे भी, उपद्रवियों f।
माइकल

0

आप यहां दो अलग-अलग चीजें कर रहे हैं: यह सुनिश्चित करना कि HashMapमौजूद है, और इसमें नई प्रविष्टि जोड़ना है।

मौजूदा कोड हैश मैप को पंजीकृत करने से पहले नए तत्व को सम्मिलित करना सुनिश्चित करता है, लेकिन यह आवश्यक नहीं है, क्योंकि HashMapयहां ऑर्डर देने की परवाह नहीं है। न तो संस्करण थ्रेडसेफ़ है, इसलिए आप कुछ भी नहीं खो रहे हैं।

तो, जैसे @Hinzi ने सुझाव दिया, आप इन दो चरणों को विभाजित कर सकते हैं।

मैं जो भी करूंगा HashMapवह allInvoicesAllClientsवस्तु के निर्माण को रोकना है , इसलिए getविधि वापस नहीं आ सकती है null

यह अलग-अलग थ्रेड्स के बीच दौड़ की संभावना को भी कम करता है जो दोनों से nullसंकेत प्राप्त कर सकते हैं getऔर फिर एक एकल प्रविष्टि के साथ एक putनए का फैसला करते हैं HashMap- दूसरा putसंभवतः Invoiceऑब्जेक्ट को खो देगा ।

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