SparseArray बनाम HashMap


177

मैं कई कारणों से सोच सकता हूं कि HashMapपूर्णांक कुंजियों के साथ SparseArrays , s की तुलना में बहुत बेहतर क्यों हैं :

  1. SparseArray"यह आम तौर पर एक पारंपरिक की तुलना में धीमा है" के लिए Android प्रलेखन HashMap
  2. यदि आप HashMaps का उपयोग करते हुए कोड लिखते हैं तो SparseArrayआपका कोड मैप के अन्य कार्यान्वयन के साथ काम करेगा और आप मैप्स के लिए डिज़ाइन किए गए सभी जावा एपीआई का उपयोग कर पाएंगे।
  3. यदि आप HashMaps के बजाय s का उपयोग करके कोड लिखते हैं तो SparseArrayआपका कोड गैर-एंड्रॉइड प्रोजेक्ट में काम करेगा।
  4. नक्शा ओवरराइड करता है equals()और hashCode()जबकि SparseArrayनहीं करता है।

फिर भी जब भी मैं HashMapएंड्रॉइड प्रोजेक्ट में पूर्णांक कुंजियों के साथ उपयोग करने का प्रयास करता हूं , तो इंटेलीज मुझे बताता है कि मुझे SparseArrayइसके बजाय उपयोग करना चाहिए । मुझे यह समझना बहुत मुश्किल है। किसी को भी SparseArrayएस का उपयोग करने के लिए कोई सम्मोहक कारण पता है ?

जवाबों:


235

SparseArrayHashMapजब कुंजी एक आदिम प्रकार है प्रतिस्थापित करने के लिए इस्तेमाल किया जा सकता है। विभिन्न कुंजी / मूल्य प्रकारों के कुछ संस्करण हैं, भले ही वे सभी सार्वजनिक रूप से उपलब्ध न हों।

लाभ हैं:

  • आवंटन से मुक्त
  • कोई बॉक्सिंग नहीं

कमियां:

  • आम तौर पर धीमी, बड़े संग्रह के लिए संकेत नहीं दिया जाता है
  • वे एक गैर-एंड्रॉइड प्रोजेक्ट में काम नहीं करेंगे

HashMap निम्नलिखित द्वारा प्रतिस्थापित किया जा सकता है:

SparseArray          <Integer, Object>
SparseBooleanArray   <Integer, Boolean>
SparseIntArray       <Integer, Integer>
SparseLongArray      <Integer, Long>
LongSparseArray      <Long, Object>
LongSparseLongArray  <Long, Long>   //this is not a public class                                 
                                    //but can be copied from  Android source code 

स्मृति के मामले में, यहाँ का एक उदाहरण है SparseIntArrayबनाम HashMap<Integer, Integer>1000 तत्वों के लिए:

SparseIntArray:

class SparseIntArray {
    int[] keys;
    int[] values;
    int size;
}

कक्षा = 12 + 3 * 4 = 24 बाइट्स
सरणी = 20 + 1000 * 4 = 4024 बाइट्स
कुल = 8,072 बाइट्स

HashMap:

class HashMap<K, V> {
    Entry<K, V>[] table;
    Entry<K, V> forNull;
    int size;
    int modCount;
    int threshold;
    Set<K> keys
    Set<Entry<K, V>> entries;
    Collection<V> values;
}

कक्षा = 12 + 8 * 4 = 48 बाइट्स
प्रविष्टि = 32 + 16 + 16 = 64 बाइट्स
सरणी = 20 + 1000 * 64 = 64024 बाइट्स
कुल = 64,136 बाइट्स

स्रोत: स्लाइड 90 से रोमेन गाई द्वारा एंड्रॉइड यादें

उपरोक्त संख्या JVM द्वारा ढेर पर आवंटित मेमोरी (बाइट्स में) की मात्रा है। वे उपयोग किए गए विशिष्ट JVM के आधार पर भिन्न हो सकते हैं।

java.lang.instrumentपैकेज के साथ एक वस्तु के आकार की जाँच जैसे उन्नत कार्यों के लिए कुछ उपयोगी तरीकों में शामिल है getObjectSize(Object objectToSize)

अतिरिक्त जानकारी आधिकारिक Oracle दस्तावेज से उपलब्ध है ।

कक्षा = 12 बाइट्स (एन उदाहरण चर) * 4 बाइट्स
ऐरे = 20 बाइट्स (एन तत्व) * (तत्व आकार)
प्रवेश = 32 बाइट्स + (प्रथम तत्व आकार) + (2 तत्व आकार)


15
क्या कोई मुझे गाइड कर सकता है जहाँ "12 + 3 * 4" और "20 + 1000 * 4" आते हैं?
मैरिएन पाडज़िओच

5
@ MarianPa Mardzioch, उन्होंने एक प्रेजेंटेशन ( स्पीकरडेक.com/romainguy/android-memories ) दिखाया, जहां एक क्लास 4 बाइट्स के 12 बाइट्स + 3 वैरिएबल्स पर कब्जा कर लेती है, एक ऐरे (रेफरेंस) पर 20 फाइट्स (dlmalloc - 4, ऑब्जेक्ट ओवरहेड - 8, चौड़ाई और पैडिंग) होती हैं। - 8)।
कूलमाइंड

1
रिकॉर्ड के लिए, SparseArray का एक और महत्वपूर्ण नुकसान यह है कि एक एंड्रॉइड ऑब्जेक्ट के रूप में इसे यूनिट परीक्षण के लिए नकली करने की आवश्यकता है। जहाँ संभव हो अब मैं परीक्षण को आसान बनाने के लिए जावा की अपनी वस्तुओं का उपयोग करता हूँ।
डेविड जी

@DavidG आप केवल एंड्रॉइड निर्भरता को मॉक करने के लिए अनमॉक प्लगइन का उपयोग कर सकते हैं ।
बर्फानी तूफान

1
यहां तक ​​कि अगर आप एंड्रॉइड नहीं कर रहे हैं, तो क्लास को अपने प्रोजेक्ट में कॉपी करना कठिन नहीं है, यह केवल 3 अन्य कक्षाओं पर निर्भर करता है। APL लाइसेंस का मतलब है कि आप जो भी लाइसेंस के साथ काम कर रहे हैं, ठीक है।
यमन टीएम

35

मैं यहाँ आया था कि कैसे उपयोग करने का एक उदाहरण चाहते हैं SparseArray। यह उस के लिए एक पूरक उत्तर है।

एक SparseArray बनाएँ

SparseArray<String> sparseArray = new SparseArray<>();

कुछ SparseArrayनक्शे पूर्णांक बनाते हैं Object, इसलिए आप Stringऊपर दिए गए उदाहरण में किसी अन्य के साथ प्रतिस्थापित कर सकते हैं Object। यदि आप पूर्णांकों को पूर्णांक मैप कर रहे हैं तो उपयोग करें SparseIntArray

आइटम जोड़ें या अपडेट करें

सरणी में तत्वों को जोड़ने के लिए put(या append) का उपयोग करें ।

sparseArray.put(10, "horse");
sparseArray.put(3, "cow");
sparseArray.put(1, "camel");
sparseArray.put(99, "sheep");
sparseArray.put(30, "goat");
sparseArray.put(17, "pig");

ध्यान दें कि intकुंजियों को क्रम में रखने की आवश्यकता नहीं है। इसका उपयोग किसी विशेष intकुंजी पर मूल्य को बदलने के लिए भी किया जा सकता है ।

आइटम हटाएँ

सरणी से तत्वों को निकालने के लिए remove(या delete) का उपयोग करें ।

sparseArray.remove(17); // "pig" removed

intपैरामीटर पूर्णांक कुंजी है।

एक इंट कुंजी के लिए लुकअप मान

getकुछ पूर्णांक कुंजी के लिए मान प्राप्त करने के लिए उपयोग करें ।

String someAnimal = sparseArray.get(99);  // "sheep"
String anotherAnimal = sparseArray.get(200); // null

get(int key, E valueIfKeyNotFound)यदि आप nullगुम चाबियों के लिए बचना चाहते हैं तो आप इसका उपयोग कर सकते हैं ।

आइटमों पर Iterate करें

आप उपयोग कर सकते हैं keyAtऔर valueAtक्योंकि संग्रह लूप करने के लिए कुछ सूचकांक SparseArrayएक अलग अनुक्रमणिका से अलग रखता है intकुंजी।

int size = sparseArray.size();
for (int i = 0; i < size; i++) {

    int key = sparseArray.keyAt(i);
    String value = sparseArray.valueAt(i);

    Log.i("TAG", "key: " + key + " value: " + value);
}

// key: 1 value: camel
// key: 3 value: cow
// key: 10 value: horse
// key: 30 value: goat
// key: 99 value: sheep

ध्यान दें कि कुंजियाँ आरोही मान में क्रमबद्ध हैं, न कि उस क्रम में जो उन्हें जोड़ा गया था।


18

फिर भी जब भी मैं किसी एंड्रॉइड प्रोजेक्ट में पूर्णांक कुंजियों के साथ एक HashMap का उपयोग करने की कोशिश करता हूं, तो intelliJ मुझे बताता है कि मुझे इसके बजाय SparseArray का उपयोग करना चाहिए।

यह विरल सरणी के इस प्रलेखन से केवल एक चेतावनी है :

इंटेगर को ऑब्जेक्ट्स में मैप करने के लिए हाशपैप का उपयोग करने की तुलना में यह अधिक मेमोरी कुशल है

यह नियमित रूप से हाशप का उपयोग करने की तुलना में मेमोरी कुशलSparseArray होने के लिए बनाया गया है , जो कि हैशपॉल की तरह नहीं सरणी के भीतर कई अंतराल की अनुमति नहीं देता है। यदि आप डिवाइस को मेमोरी आवंटन के बारे में चिंता नहीं करना चाहते हैं, तो इसके बारे में चिंता करने की कोई बात नहीं है।


5
मेमोरी को बचाने के बारे में बिंदु स्पष्ट रूप से मान्य हैं, लेकिन मैंने कभी नहीं समझा कि एंड्रॉइड SparseArray <T> को लागू नहीं कर सका मानचित्र <Integer, T> ताकि आप एक मेमोरी कुशल मैप कार्यान्वयन प्राप्त करें - दोनों दुनिया के सर्वश्रेष्ठ।
पॉल बोडिंगटन

3
@PaBBoddington को यह भी याद SparseArrayहै कि कुंजी पूर्णांक को ऑटो बॉक्स होने से रोकता है जो एक अन्य ऑपरेशन और लागत प्रदर्शन है। मानचित्र के बजाय यह आदिम पूर्णांक को ऑटोबॉक्स करेगाInteger
रॉड_ ऑलगिनक्विन

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

6
@PaBBoddington कलेक्शंस आदिम नहीं वस्तुओं पर आधारित हैं, इसलिए यह कलेक्शन एपीआई के भीतर काम नहीं करेगा
Rod_Algonquin

10

जावा में एक विरल सरणी एक डेटा संरचना है जो मानों के लिए कुंजी मैप करती है। मानचित्र के रूप में समान विचार, लेकिन विभिन्न कार्यान्वयन:

  1. मानचित्र को सूचियों के एक सरणी के रूप में आंतरिक रूप से दर्शाया जाता है, जहां इन सूचियों में प्रत्येक तत्व एक कुंजी, मूल्य जोड़ी है। दोनों कुंजी और मूल्य वस्तु उदाहरण हैं।

  2. एक विरल सरणी केवल दो सरणियों से बना है: (आदिम) कुंजियों का एक सारणी और (ऑब्जेक्ट्स) मानों की एक सरणी। इन सरणियों सूचकांकों में अंतराल हो सकते हैं, इसलिए शब्द "विरल" सरणी।

SparseArray का मुख्य हित यह है कि यह कुंजी के रूप में वस्तुओं के बजाय आदिम का उपयोग करके स्मृति को बचाता है।


10

कुछ googling के बाद मैं पहले से ही पोस्ट किए गए awers में कुछ जानकारी जोड़ने की कोशिश करता हूं:

आइजैक टेलर ने स्पार्सअरीस और हैशमैप के लिए एक प्रदर्शन तुलना की। उसने व्यक्त किया की

हशमैप और स्पैर्सआयर 1,000 के तहत डेटा संरचना के आकार के लिए बहुत समान हैं

तथा

जब आकार को 10,000 के निशान तक बढ़ा दिया गया है [...] वस्तुओं को जोड़ने के साथ हैशमैप का प्रदर्शन अधिक होता है, जबकि SparseArray में वस्तुओं को पुनः प्राप्त करते समय अधिक प्रदर्शन होता है। [...] १,००,००० के आकार पर [...] हशमैप बहुत जल्दी प्रदर्शन खो देता है

एजलब्लॉग पर एक तुलना से पता चलता है कि स्पार्सअरे को हेशपप की तुलना में बहुत कम मेमोरी की आवश्यकता होती है क्योंकि छोटी कुंजी (इंट बनाम इंटीजर) और तथ्य यह है कि

एक HashMap.Entry उदाहरण कुंजी, मान और अगली प्रविष्टि के लिए संदर्भों का ट्रैक रखना चाहिए। साथ ही इसे इंट के रूप में हैश को स्टोर करने की भी आवश्यकता है।

एक निष्कर्ष के रूप में मैं कहूंगा कि यदि आप अपने मानचित्र में बहुत सारे डेटा संग्रहीत करने जा रहे हैं तो यह अंतर महत्वपूर्ण हो सकता है। अन्यथा, सिर्फ चेतावनी को नजरअंदाज करें।


4

SparseArray के लिए Android दस्तावेज़ कहता है "यह आम तौर पर एक पारंपरिक हैशपेयर की तुलना में धीमी है"।

हाँ यह सही है। लेकिन जब आपके पास केवल 10 या 20 आइटम हों, तो प्रदर्शन अंतर महत्वहीन होना चाहिए।

यदि आप SparseArays के बजाय HashMaps का उपयोग करके कोड लिखते हैं तो आपका कोड मैप के अन्य कार्यान्वयन के साथ काम करेगा और आप मैप्स के लिए डिज़ाइन किए गए सभी जावा APIs का उपयोग करने में सक्षम होंगे

मुझे लगता है कि अक्सर हम केवल HashMapएक कुंजी के साथ जुड़े मूल्य को खोजने के लिए उपयोग करते हैं जबकि SparseArrayयह वास्तव में अच्छा है।

यदि आप SparseArrays के बजाय HashMaps का उपयोग करके कोड लिखते हैं तो आपका कोड गैर-एंड्रॉइड प्रोजेक्ट में काम करेगा।

SparseArray का स्रोत कोड काफी सरल और समझने में आसान है ताकि आप केवल इसे अन्य प्लेटफार्मों पर ले जाने के लिए थोड़े प्रयास का भुगतान करें (एक साधारण COPY और पेस्ट के माध्यम से)।

मानचित्र बराबर () और हैशकोड () है जबकि SparseArray नहीं है

सभी मैं कह सकता हूँ (सबसे डेवलपर्स के लिए) जो परवाह है?

का एक अन्य महत्वपूर्ण पहलू SparseArrayयह है कि यह केवल जबकि सभी तत्वों को स्टोर करने के लिए एक सरणी का उपयोग करता है HashMapका उपयोग करता है Entry, तो SparseArrayएक से महत्वपूर्ण कम स्मृति की लागत HashMap, देखना यह


1

यह दुर्भाग्यपूर्ण है कि संकलक एक चेतावनी जारी करता है। मुझे लगता है कि HashMap वस्तुओं के भंडारण के लिए अत्यधिक उपयोग किया गया है।

SparseArrays का अपना स्थान है। यह देखते हुए कि वे एक सरणी में एक मूल्य खोजने के लिए एक द्विआधारी खोज एल्गोरिथ्म का उपयोग करते हैं, आपको विचार करना होगा कि आप क्या कर रहे हैं। बाइनरी खोज हे (लॉग एन) है जबकि हैश लुकिंग ओ (1) है। यह जरूरी नहीं कि बाइनरी सर्च डेटा के दिए गए सेट के लिए धीमा हो। हालाँकि, जैसे ही प्रविष्टियों की संख्या बढ़ती है, हैश टेबल की शक्ति खत्म हो जाती है। इसलिए टिप्पणियाँ जहाँ प्रविष्टियों की कम संख्या बराबर हो सकती है और संभवतः एक HashMap का उपयोग करने से बेहतर हो सकती है।

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

मेरा सुझाव है कि अगर आपको हैश तालिका की आवश्यकता है और आदिम पूर्णांक (कोई ऑटो मुक्केबाजी) आदि के लिए बेहतर मेमोरी उपयोग चाहते हैं, तो ट्राव आउट करें। ( http://trove.starlight-systems.com - LGPL लाइसेंस)। (उनके पुस्तकालय की तरह, ट्रोव के साथ कोई संबंध नहीं)

सरलीकृत मल्टी-डेक्स बिल्डिंग के साथ हमारे पास आपकी आवश्यकता के लिए ट्रंक को फिर से तैयार करने की आवश्यकता नहीं है। (ट्रोव के पास बहुत सारी कक्षाएं हैं)

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