गैर-पाया कुंजी के लिए डिफ़ॉल्ट मान वापस करने के लिए हैशपॉप?


151

क्या HashMapसभी कुंजियों के लिए डिफ़ॉल्ट मान होना संभव है जो सेट में नहीं मिले हैं?


आप मुख्य अस्तित्व और डिफ़ॉल्ट के लिए जाँच कर सकते हैं। या कक्षा का विस्तार करें और व्यवहार को संशोधित करें। या यहां तक ​​कि आप अशक्त का उपयोग कर सकते हैं - और जहाँ भी आप इसका उपयोग करना चाहते हैं, वहाँ कुछ जाँच डालें।
सुधीरज

2
यह संबंधित है / stackoverflow.com/questions/4833336/… के डुप्लिकेट पर कुछ अन्य विकल्पों पर चर्चा की गई है।
मार्क बटलर

3
मानचित्र एपीआई getOrDefault() लिंक के
ट्रे जुन्न

जवाबों:


136

[अपडेट करें]

जैसा कि अन्य उत्तर और टिप्पणीकारों ने कहा है, जावा 8 के रूप में आप बस कॉल कर सकते हैं Map#getOrDefault(...)

[मूल रूप]

कोई मानचित्र कार्यान्वयन नहीं है जो इसे ठीक करता है, लेकिन हैशपॉप का विस्तार करके अपने स्वयं को लागू करना तुच्छ होगा:

public class DefaultHashMap<K,V> extends HashMap<K,V> {
  protected V defaultValue;
  public DefaultHashMap(V defaultValue) {
    this.defaultValue = defaultValue;
  }
  @Override
  public V get(Object k) {
    return containsKey(k) ? super.get(k) : defaultValue;
  }
}

20
बस सटीक होना करने के लिए, आप से हालत समायोजित करने के लिए चाहते हो सकता है (v == null)के लिए (v == null && !this.containsKey(k))मामले में वे जानबूझकर एक जोड़ा nullमूल्य। मुझे पता है, यह सिर्फ एक कोने का मामला है, लेकिन लेखक इसमें भाग सकता है।
एडम पेन्न्टर

@ कामिक्स: मैंने देखा कि आपने इस्तेमाल किया !this.containsValue(null)। यह सूक्ष्म रूप से भिन्न है !this.containsKey(k)containsValueसमाधान यदि कुछ असफल हो जायेगी अन्य कुंजी स्पष्ट रूप से एक मूल्य सौंपा गया है null। उदाहरण के लिए: map = new HashMap(); map.put(k1, null); V v = map.get(k2);इस मामले में, vअभी भी nullसही होगा?
एडम पेन्नटर

21
सामान्य तौर पर , मुझे लगता है कि यह एक बुरा विचार है - मैं क्लाइंट में डिफ़ॉल्ट व्यवहार, या एक प्रतिनिधि को धक्का दे दूंगा जो मानचित्र होने का दावा नहीं करता है। विशेष रूप से, वैध कीसेट () या एंट्रीसेट () की कमी से कुछ भी समस्या पैदा हो जाएगी, जो मैप के अनुबंध का सम्मान करने की अपेक्षा करती है। और मान्य कुंजियों का अनंत सेट जिसमें निहित है () का अर्थ है खराब प्रदर्शन का कारण है जो निदान करना मुश्किल है। हालांकि, यह कहना नहीं है कि यह कुछ विशेष उद्देश्य की पूर्ति नहीं कर सकता है।
एड स्टाब

इस दृष्टिकोण के साथ एक समस्या यह है कि मान एक जटिल वस्तु है। नक्शा <स्ट्रिंग, सूची> #पुट अपेक्षित के अनुसार काम नहीं करेगा।
ईयाल

ConcurrentHashMap पर काम नहीं करता है। वहां, आपको अशक्त के लिए परिणाम प्राप्त करने की जांच करनी चाहिए।
डेविम

162

जावा 8 में, Map.getOrDefault का उपयोग करें । यह कुंजी लेता है, और कोई मिलान कुंजी नहीं मिलने पर वापस जाने के लिए मान।


14
getOrDefaultबहुत अच्छा है, लेकिन हर बार नक्शे तक पहुँचने के लिए डिफ़ॉल्ट परिभाषा की आवश्यकता होती है। मूल्यों का स्थैतिक मानचित्र बनाते समय डिफ़ॉल्ट मान को एक बार परिभाषित करने से पठनीयता लाभ भी होगा।
ACH

3
यह अपने आप को लागू करने के लिए तुच्छ है। private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
जैक Satriano

@JackSatriano हाँ, लेकिन आपको डिफ़ॉल्ट मान को हार्ड-कोड करना होगा, या इसके लिए एक स्थिर चर होगा।
Blrp

1
ComputeIfAbsent का उपयोग करके उत्तर के नीचे देखें, बेहतर तब होता है जब डिफ़ॉल्ट मान महंगा हो या हर बार अलग होना चाहिए।
हेक्टरपाल

यद्यपि यह मेमोरी के लिए बदतर है, और केवल गणना समय की बचत करेगा यदि डिफ़ॉल्ट मान निर्माण / गणना करने के लिए महंगा है। यदि यह सस्ता है, तो आप शायद इसे और भी बुरा प्रदर्शन करेंगे, क्योंकि इसे केवल एक डिफ़ॉल्ट मान वापस करने के बजाय मानचित्र में सम्मिलित करना होगा। हालांकि एक और विकल्प।
स्पाइको

73

यदि आप पहिया को फिर से स्थापित करने की तरह महसूस नहीं करते हैं, तो कॉमन्स के डिफॉल्टपाइप का उपयोग करें , जैसे,

Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname"); 
// surname == "[NO ENTRY FOUND]"

यदि आप पहले स्थान पर नक्शा बनाने के प्रभारी नहीं हैं, तो आप एक मौजूदा नक्शे में भी पास कर सकते हैं।


26
+1 हालांकि कभी-कभी यह सरल कार्यक्षमता के छोटे स्लाइस के लिए बड़ी निर्भरता का परिचय देने की तुलना में पहिया को फिर से मजबूत करना है।
मैरिकिक्स

3
और मजेदार बात यह है, कि मैं कई परियोजनाओं के साथ काम करता हूं, जो
क्लासपैथ

@ bartosz.r, मोबाइल पर निश्चित रूप से नहीं
Pacerier

44

जावा 8 ने इंटरफ़ेस के लिए एक अच्छा कम्प्यूटेशनल डिफ़ॉल्ट डिफॉल्ट विधि पेश की, Mapजो आलसी-गणना मूल्य को संग्रहीत करता है और इसलिए नक्शा अनुबंध को नहीं तोड़ता है:

Map<Key, Graph> map = new HashMap<>();
map.computeIfAbsent(aKey, key -> createExpensiveGraph(key));

उत्पत्ति: http://blog.javabien.net/2014/02/20/loadingcache-in-java-8-without-guava/

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


ओपी ने जो नहीं पूछा: वह नक्शे पर कोई दुष्प्रभाव नहीं चाहता है। इसके अलावा, प्रत्येक अनुपस्थित कुंजी के लिए डिफ़ॉल्ट मान संग्रहीत करना मेमोरी स्पेस का एक बेकार नुकसान है।
numéro6

@ numéro6, हां, यह बिल्कुल वैसा नहीं है जैसा कि ओपी ने पूछा था लेकिन कुछ गुगली वाले लोग अभी भी इस पक्ष को उपयोगी पाते हैं। जैसा कि अन्य उत्तरों में उल्लेख किया गया है, यह ठीक उसी तरह से हासिल करना असंभव है, जो ओपी ने बिना नक्शे के अनुबंध को तोड़ने के लिए कहा था। यहाँ उल्लेख नहीं किया गया एक और वर्कअराउंड मैप के बजाय एक और अमूर्त का उपयोग करना है
वडज़िम

यह ठीक उसी तरह हासिल करना संभव है जो ओपी ने बिना नक्शे के अनुबंध को तोड़ने के लिए कहा। कोई वर्कअराउंड की आवश्यकता नहीं है, बस getOrDefault का उपयोग करना सही (सबसे अपडेट किया गया) तरीका है, computeIfAbsent गलत तरीका है: आप परिणाम को मैप करने के लिए मैपिंगफंक्शन और मेमोरी को कॉल करने में समय खो देंगे (दोनों प्रत्येक अतिरिक्त कुंजी के लिए)। मैं getOrDefault के बजाय ऐसा करने का कोई अच्छा कारण नहीं देख सकता। मैं जो वर्णन कर रहा हूं वह सटीक कारण है कि मैप अनुबंध पर दो अलग-अलग विधियां हैं: दो अलग-अलग उपयोग-मामले हैं जिन्हें भ्रमित नहीं किया जाना चाहिए (मुझे काम पर कुछ ठीक करना था)। इस जवाब ने भ्रम फैलाया।
numéro6

14

क्या आप सिर्फ एक स्थैतिक विधि नहीं बना सकते हैं जो वास्तव में ऐसा करती है?

private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
    return map.containsKey(key) ? map.get(key) : defaultValue;
}

जहां स्थिर करने के लिए स्टोर?
Pacerier

10

आप बस एक नया वर्ग बना सकते हैं जो HashMap को इनहेरिट करता है और getDefault मेथड जोड़ता है। यहाँ एक नमूना कोड है:

public class DefaultHashMap<K,V> extends HashMap<K,V> {
    public V getDefault(K key, V defaultValue) {
        if (containsKey(key)) {
            return get(key);
        }

        return defaultValue;
    }
}

मुझे लगता है कि आपको अपने कार्यान्वयन में कारण (K कुंजी) विधि को ओवरराइड नहीं करना चाहिए, क्योंकि एड स्टॉब ने उनकी टिप्पणी में निर्दिष्ट कारणों के कारण और क्योंकि आप मैप इंटरफ़ेस के अनुबंध को तोड़ देंगे (यह संभवतः कुछ हार्ड-टू-फाइंड को जन्म दे सकता है कीड़े)।


4
आपको getविधि को ओवरराइड नहीं करने का एक बिंदु मिला है । दूसरी ओर - आपका समाधान इंटरफ़ेस के माध्यम से कक्षा का उपयोग करने की अनुमति नहीं देता है, जो अक्सर मामला हो सकता है।
Krzysztof Jabłoński


3

यह डिफ़ॉल्ट रूप से ऐसा करता है। यह लौट आता है null


@ लॉरी, मुझे HashMapइस कार्यक्षमता के लिए थोड़ा उपहास करने के लिए थोड़ा मूर्खतापूर्ण लगता है जब nullपूरी तरह से ठीक है।
mrkhrts

15
यह ठीक नहीं है यदि आप एक NullObjectपैटर्न का उपयोग कर रहे हैं , हालांकि, या अपने कोड में अशक्त-चेक को बिखेरना नहीं चाहते हैं - एक इच्छा जिसे मैं पूरी तरह से समझता हूं।
डेव न्यूटन


1

मुझे LazyMap काफी मददगार लगा।

जब एक ऑब्जेक्ट के साथ गेट (ऑब्जेक्ट) विधि को बुलाया जाता है जो नक्शे में मौजूद नहीं होता है, तो फैक्ट्री का उपयोग ऑब्जेक्ट बनाने के लिए किया जाता है। अनुरोधित कुंजी का उपयोग करके बनाई गई वस्तु को मानचित्र में जोड़ा जाएगा।

यह आपको कुछ इस तरह से करने की अनुमति देता है:

    Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
    map.get(notExistingKey).incrementAndGet();

getदी गई कुंजी के लिए डिफ़ॉल्ट मान बनाने के लिए कॉल । आप निर्दिष्ट करें कि फ़ैक्टरी तर्क के साथ डिफ़ॉल्ट मान कैसे बनाया जाए LazyMap.lazyMap(map, factory)। ऊपर दिए गए उदाहरण में, मानचित्र को AtomicIntegerमान 0 के साथ एक नए से आरंभ किया गया है ।


इसमें स्वीकार किए गए उत्तर पर एक फायदा है कि डिफ़ॉल्ट मूल्य एक कारखाने द्वारा बनाया गया है। क्या होगा यदि मेरा डिफ़ॉल्ट मान है List<String>- स्वीकृत उत्तर का उपयोग करके मैं एक कारखाने से प्रत्येक नई कुंजी के लिए एक ही सूची का उपयोग करने का जोखिम उठाऊंगा, (कहो) new ArrayList<String>()

0

सीधे तौर पर नहीं, लेकिन आप इसकी प्राप्त विधि को संशोधित करने के लिए कक्षा का विस्तार कर सकते हैं। यहाँ उदाहरण का उपयोग करने के लिए तैयार है: http://www.java2s.com/Code/Java/Collections-Data-Structure/ExtendedVersionofjavautilHashMapthatprovidesanextendedgetmethgetaccacingingadefaultvalue.htm


0
/**
 * Extension of TreeMap to provide default value getter/creator.
 * 
 * NOTE: This class performs no null key or value checking.
 * 
 * @author N David Brown
 *
 * @param <K>   Key type
 * @param <V>   Value type
 */
public abstract class Hash<K, V> extends TreeMap<K, V> {

    private static final long serialVersionUID = 1905150272531272505L;

    /**
     * Same as {@link #get(Object)} but first stores result of
     * {@link #create(Object)} under given key if key doesn't exist.
     * 
     * @param k
     * @return
     */
    public V getOrCreate(final K k) {
        V v = get(k);
        if (v == null) {
            v = create(k);
            put(k, v);
        }
        return v;
    }

    /**
     * Same as {@link #get(Object)} but returns specified default value
     * if key doesn't exist. Note that default value isn't automatically
     * stored under the given key.
     * 
     * @param k
     * @param _default
     * @return
     */
    public V getDefault(final K k, final V _default) {
        V v = get(k);
        return v == null ? _default : v;
    }

    /**
     * Creates a default value for the specified key.
     * 
     * @param k
     * @return
     */
    abstract protected V create(final K k);
}

उदाहरण उपयोग:

protected class HashList extends Hash<String, ArrayList<String>> {
    private static final long serialVersionUID = 6658900478219817746L;

    @Override
        public ArrayList<Short> create(Short key) {
            return new ArrayList<Short>();
        }
}

final HashList haystack = new HashList();
final String needle = "hide and";
haystack.getOrCreate(needle).add("seek")
System.out.println(haystack.get(needle).get(0));

0

मुझे JSON में एक सर्वर से लौटाए गए परिणामों को पढ़ने की आवश्यकता है जहां मैं गारंटी नहीं दे सकता कि फ़ील्ड मौजूद होंगे। मैं class org.json.simple.JSONObject का उपयोग कर रहा हूं, जो HashMap से लिया गया है। यहाँ कुछ सहायक कार्य दिए गए हैं:

public static String getString( final JSONObject response, 
                                final String key ) 
{ return getString( response, key, "" ); }  
public static String getString( final JSONObject response, 
                                final String key, final String defVal ) 
{ return response.containsKey( key ) ? (String)response.get( key ) : defVal; }

public static long getLong( final JSONObject response, 
                            final String key ) 
{ return getLong( response, key, 0 ); } 
public static long getLong( final JSONObject response, 
                            final String key, final long defVal ) 
{ return response.containsKey( key ) ? (long)response.get( key ) : defVal; }

public static float getFloat( final JSONObject response, 
                              final String key ) 
{ return getFloat( response, key, 0.0f ); } 
public static float getFloat( final JSONObject response, 
                              final String key, final float defVal ) 
{ return response.containsKey( key ) ? (float)response.get( key ) : defVal; }

public static List<JSONObject> getList( final JSONObject response, 
                                        final String key ) 
{ return getList( response, key, new ArrayList<JSONObject>() ); }   
public static List<JSONObject> getList( final JSONObject response, 
                                        final String key, final List<JSONObject> defVal ) { 
    try { return response.containsKey( key ) ? (List<JSONObject>) response.get( key ) : defVal; }
    catch( ClassCastException e ) { return defVal; }
}   

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