मैप का उपयोग करते समय java Map.containsKey () निरर्थक का उपयोग कर रहा है ()


93

मैं कुछ समय से सोच रहा था कि क्या इस containsKey()पद्धति का उपयोग करने से बचना सर्वोत्तम अभ्यास के भीतर स्वीकार्य है java.util.Mapऔर इसके बजाय परिणाम पर एक अशक्त जाँच करें get()

मेरा तर्क यह है कि मूल्य को दो बार देखने के लिए यह बेमानी लगता है - पहली बार containsKey()और फिर दोबारा get()

दूसरी ओर यह हो सकता है कि Mapकैश के अधिकांश मानक कार्यान्वयन अंतिम लुकअप या संकलक अन्यथा अतिरेक के साथ दूर कर सकते हैं, और कोड की पठनीयता के लिए यह containsKey()हिस्सा बनाए रखने के लिए बेहतर है ।

मैं आपकी टिप्पणियों की बहुत सराहना करूंगा।

जवाबों:


112

कुछ मानचित्र कार्यान्वयन को शून्य मान रखने की अनुमति है, उदाहरण के लिए, हाशपैप, इस मामले में यदि get(key)रिटर्न देता nullहै तो यह गारंटी नहीं देता है कि इस कुंजी के साथ जुड़े नक्शे में कोई प्रविष्टि नहीं है।

इसलिए यदि आप जानना चाहते हैं कि क्या मानचित्र में एक महत्वपूर्ण उपयोग है Map.containsKey। यदि आपको बस एक महत्वपूर्ण उपयोग के लिए मैप किए गए मान की आवश्यकता है Map.get(key)। यदि यह मानचित्र शून्य मानों की अनुमति देता है, तो नल का एक वापसी मूल्य जरूरी नहीं दर्शाता है कि नक्शे में कुंजी के लिए कोई मैपिंग नहीं है; ऐसे मामले Map.containsKeyमें बेकार है और प्रदर्शन को प्रभावित करेगा। इसके अलावा, एक नक्शे (जैसे ConcurrentHashMap) के समवर्ती उपयोग के मामले में , आपके द्वारा परीक्षण किए जाने के बाद Map.containsKey(key)एक मौका है कि आपके कॉल करने से पहले प्रविष्टि को किसी अन्य थ्रेड द्वारा हटा दिया जाएगा Map.get(key)


8
यदि मान निर्धारित किया गया है null, तो भी क्या आप उस कुंजी / मान के लिए अलग तरीके से व्यवहार करना चाहते हैं जो सेट नहीं है? आप विशेष रूप से इसे दूसरे तरीके से इलाज करने के लिए की जरूरत नहीं है, तो आप बस का उपयोग कर सकतेget()
पीटर Lawrey

1
यदि आपका Mapहै private, तो आपकी कक्षा इस बात की गारंटी देने में सक्षम हो सकती nullहै कि वह नक्शे में कभी सम्मिलित नहीं है। उस स्थिति में, आप get()इसके बजाय null के लिए चेक का उपयोग कर सकते हैं containsKey()। ऐसा करना स्पष्ट हो सकता है, और शायद कुछ मामलों में थोड़ा अधिक कुशल।
Raedwald

44

मुझे लगता है कि यह लिखने के लिए काफी मानक है:

Object value = map.get(key);
if (value != null) {
    //do something with value
}

के बजाय

if (map.containsKey(key)) {
    Object value = map.get(key);
    //do something with value
}

यह कम पठनीय और थोड़ा अधिक कुशल नहीं है, इसलिए मुझे ऐसा नहीं करने के लिए कोई कारण नहीं दिखता है। जाहिर है कि अगर आपके नक्शे में अशक्तता हो सकती है, तो दो विकल्पों में समान शब्दार्थ नहीं है


8

जैसा कि संकेत दिया गया है, यह एक अर्थपूर्ण प्रश्न है। आम तौर पर, Map.get (x) == null वही होता है जो आप चाहते हैं, लेकिन ऐसे मामले भी हैं जिनमें ContKey का उपयोग करना महत्वपूर्ण है।

ऐसा ही एक मामला कैश का है। मैंने एक बार एक वेब ऐप में एक प्रदर्शन के मुद्दे पर काम किया था जो अपने डेटाबेस को क्वेरी कर रहा था जो अक्सर अस्तित्व में नहीं थे। जब मैंने उस घटक के लिए कैशिंग कोड का अध्ययन किया, तो मुझे एहसास हुआ कि अगर यह cache.get (key) == null डेटाबेस से क्वेरी कर रहा है। यदि डेटाबेस अशक्त (इकाई नहीं मिली) लौटा, तो हम उस कुंजी -> नल मैपिंग को कैश कर देंगे।

Contey में स्विच करने से समस्या हल हो गई क्योंकि एक शून्य मान के लिए मैपिंग वास्तव में कुछ मतलब था। अशक्त करने के लिए कुंजी मैपिंग का अर्थ मौजूदा नहीं होने की तुलना में एक अलग अर्थ है।


दिलचस्प। कैशिंग मूल्यों से पहले आपने बस एक शून्य जांच क्यों नहीं जोड़ी?
साकेत

यह कुछ भी नहीं बदलेगा। मुद्दा यह है कि अशक्त साधन करने के लिए कुंजी मानचित्रण "हम पहले से ही यह किया है। यह कैश की गई है। मूल्य है अशक्त"। वर्सस में दी गई कुंजी नहीं है, जिसका अर्थ है "न जाने, कैश में नहीं, हमें DB की जांच करने की आवश्यकता हो सकती है।"
ब्रैंडन

5
  • containsKeyएक के बाद getबेमानी है अगर हम एप्रीओरी जानते हैं कि अशक्त मूल्यों की अनुमति कभी नहीं होगी। यदि शून्य मान मान्य नहीं हैं, तो containsKeyएक गैर-तुच्छ प्रदर्शन जुर्माना के आह्वान पर है और नीचे दिए गए बेंचमार्क में दिखाया गया है।

  • जावा 8 Optionalमुहावरे - Optional.ofNullable(map.get(key)).ifPresentया Optional.ofNullable(map.get(key)).ifPresent- केवल वैनिला नल की जाँच के साथ एक गैर-तुच्छ ओवरहेड को उकसाते हैं।

  • एक HashMapएक का उपयोग करता है O(1)निरंतर तालिका देखने एक जबकि TreeMapका उपयोग करता है एक O(log(n))देखने। containsKeyएक के बाद getमुहावरा बहुत धीमी है जब एक पर लागू है TreeMap

मानक

Https://github.com/vkarun/enum-reverse-lookup-table-jmh देखें

// t1
static Type lookupTreeMapNotContainsKeyThrowGet(int t) {
  if (!lookupT.containsKey(t))
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return lookupT.get(t);
}
// t2
static Type lookupTreeMapGetThrowIfNull(int t) {
  Type type = lookupT.get(t);
  if (type == null)
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return type;
}
// t3
static Type lookupTreeMapGetOptionalOrElseThrow(int t) {
  return Optional.ofNullable(lookupT.get(t)).orElseThrow(() -> new 
      IllegalStateException("Unknown Multihash type: " + t));
}
// h1
static Type lookupHashMapNotContainsKeyThrowGet(int t) {
  if (!lookupH.containsKey(t))
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return lookupH.get(t);
}
// h2
static Type lookupHashMapGetThrowIfNull(int t) {
  Type type = lookupH.get(t);
  if (type == null)
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return type;
}
// h3
static Type lookupHashMapGetOptionalOrElseThrow(int t) {
  return Optional.ofNullable(lookupH.get(t)).orElseThrow(() -> new 
    IllegalStateException("Unknown Multihash type: " + t));
}
बेंचमार्क (पुनरावृत्तियों) (लुकअपऐपॉर्च) मोड Cnt स्कोर त्रुटि इकाइयाँ

MultihashTypeLookupBenchmark.testLookup 1000 t1 avgt 9 33.438 op 4.514 / op
MultihashTypeLookupBenchmark.testLookup 1000 t2 avgt 9 26.986 / 0.405 us / op
MultihashTypeLookupBenchmark.testLookup 1000 t3 avgt 9 39.259 / 1.306 us / op
MultihashTypeLookupBenchmark.testLookup 1000 h1 avgt 9 18.954 op 0.414 / op
MultihashTypeLookupBenchmark.testLookup 1000 h2 avgt 9 15.486 / 0.395 us / op
MultihashTypeLookupBenchmark.testLookup 1000 h3 avgt 9 16.780 op 0.719 / op

ट्रीपॉपर स्रोत संदर्भ

https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/TreeMap.java

HashMap स्रोत संदर्भ

https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/HashMap.java


3

हम जावा 8 वैकल्पिक के साथ @ पठनीय उत्तर को अधिक पठनीय बना सकते हैं,

Optional.ofNullable(map.get(key)).ifPresent(value -> {
     //do something with value
};)

2

जावा में यदि आप कार्यान्वयन की जाँच करते हैं

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

दोनों मिलान प्राप्त करने के लिए getNode का उपयोग करते हैं, जहां मुख्य काम हो जाता है।

अतिरेक उदाहरण के लिए प्रासंगिक है यदि आपके पास कोई शब्दकोश हैश मानचित्र में संग्रहीत है। जब आप किसी शब्द का अर्थ प्राप्त करना चाहते हैं

करते हुए...

if(dictionary.containsKey(word)) {
   return dictionary.get(word);
}

बेमानी है।

लेकिन अगर आप किसी शब्द की जाँच करना चाहते हैं तो वह मान्य है या शब्दकोश पर आधारित नहीं है। करते हुए...

 return dictionary.get(word) != null;

ऊपर...

 return dictionary.containsKey(word);

बेमानी है।

यदि आप HashSet कार्यान्वयन की जांच करते हैं , जो आंतरिक रूप से HashMap का उपयोग करता है, तो 'समाहित' 'में' विधि का उपयोग करें।

    public boolean contains(Object o) {
        return map.containsKey(o);
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.