2009-06-29 18 views
61

के रूप में बाइट सरणी का उपयोग करना क्या आपको मानचित्र कुंजी के रूप में बाइट सरणी का उपयोग करने में कोई समस्या दिखाई देती है? मैं new String(byte[]) और हैश String द्वारा भी कर सकता हूं लेकिन यह byte[] का उपयोग करने के लिए अधिक सरल है।मैप कुंजी

उत्तर

51

समस्या यह है कि byte[] का उपयोग करता है equals और hashCode के लिए पहचान आपत्ति है, इसलिए कि

byte[] b1 = {1, 2, 3} 
byte[] b2 = {1, 2, 3} 

HashMap में मेल नहीं खाएगा। मैं तीन विकल्प देखें: एक String में

  1. रैपिंग, लेकिन फिर आप मुद्दों एन्कोडिंग के बारे में सावधान रहना होगा (-> स्ट्रिंग -> बाइट आप एक ही बाइट्स देता है आप कुछ करने के लिए है कि बाइट की जरूरत है)।
  2. List<Byte> का उपयोग करें (स्मृति में महंगा हो सकता है)।
  3. बाइट सरणी की सामग्री का उपयोग करने के लिए hashCode और equals लिखकर अपनी खुद की रैपिंग कक्षा करें।
+3

मैंने हेक्स-एन्कोडिंग का उपयोग करके स्ट्रिंग-रैपिंग समस्या हल की। आप वैकल्पिक रूप से बेस 64 एन्कोडिंग का उपयोग कर सकते हैं। – metadaddy

+0

रैपिंग/हैंडलिंग क्लास विकल्प सीधा है और बहुत पठनीय होना चाहिए। – ZX9

1

मेरा मानना ​​है कि जावा में सरणी hashCode() और equals(Object) विधियों को सहजता से कार्यान्वित नहीं करती हैं। यही है, दो समान बाइट एरे जरूरी नहीं है कि वे एक ही हैश कोड साझा करें और वे जरूरी नहीं होने का दावा करेंगे। इन दो लक्षणों के बिना, आपका हैश मैप अप्रत्याशित रूप से व्यवहार करेगा।

इसलिए, मैं के विरुद्ध byte[] का उपयोग हैश मैप में कुंजी के रूप में करता हूं।

+4

साथ प्रासंगिक नहीं है s/जरूरी नहीं/नहीं/ –

+0

मुझे लगता है मेरी शब्दों थोड़ा बंद किया गया था। मैं उस स्थिति के लिए लेखांकन कर रहा था जहां हैश नक्शा में सम्मिलन के लिए और हैश मानचित्र से पुनर्प्राप्ति के लिए समान बाइट सरणी का उपयोग किया जा रहा है। उस स्थिति में, "दोनों" बाइट एरे समान हैं और समान हैश कोड साझा करते हैं। –

71

यह ठीक है जब तक कि आप केवल अपनी कुंजी के लिए संदर्भ समानता चाहते हैं - सरणी उस तरीके से "मूल्य समानता" को लागू नहीं करती है जिसकी आप शायद चाहें।

आप मान लिया जाये कि वास्तव में समानता चाहते हैं;

false 
1671711 
11394033 

(। तथ्य यह है कि वे अलग हैं महत्वपूर्ण है वास्तविक संख्या अप्रासंगिक हैं):

byte[] array1 = new byte[1]; 
byte[] array2 = new byte[1]; 

System.out.println(array1.equals(array2)); 
System.out.println(array1.hashCode()); 
System.out.println(array2.hashCode()); 

प्रिंट कुछ की तरह: उदाहरण के लिए , मेरा सुझाव है कि आप अपना खुद का रैपर बनाएं जिसमें byte[] शामिल है और समानता और हैश कोड पीढ़ी को उचित रूप से लागू करता है:

public final class ByteArrayWrapper 
{ 
    private final byte[] data; 

    public ByteArrayWrapper(byte[] data) 
    { 
     if (data == null) 
     { 
      throw new NullPointerException(); 
     } 
     this.data = data; 
    } 

    @Override 
    public boolean equals(Object other) 
    { 
     if (!(other instanceof ByteArrayWrapper)) 
     { 
      return false; 
     } 
     return Arrays.equals(data, ((ByteArrayWrapper)other).data); 
    } 

    @Override 
    public int hashCode() 
    { 
     return Arrays.hashCode(data); 
    } 
} 

ध्यान दें कि आप में ByteArrayWrapper उपयोग करने के बाद बाइट सरणी के भीतर मान बदलते हैं, एक प्रमुख के रूप में अगर एक HashMap (आदि) आप समस्याओं फिर से कुंजी को देख होगा ... आप की एक प्रति ले सकता है यदि आप चाहते हैं तो ByteArrayWrapper कन्स्ट्रक्टर में डेटा, लेकिन स्पष्ट रूप से यह प्रदर्शन की बर्बादी होगी यदि आपको पता है कि बाइट सरणी की सामग्री को बदल नहीं रहा है।

संपादित करें: टिप्पणियों में उल्लिखित अनुसार, आप इसके लिए ByteBuffer का उपयोग भी कर सकते हैं (विशेष रूप से, इसकी ByteBuffer#wrap(byte[]) विधि)। मुझे नहीं पता कि यह वास्तव में सही बात है, ByteBuffer की सभी अतिरिक्त क्षमताओं को देखते हुए आपको इसकी आवश्यकता नहीं है, लेकिन यह एक विकल्प है।

+0

@ डीएफए: "उदाहरण" परीक्षण शून्य मामले को संभालता है। –

+3

रैपर कार्यान्वयन में आप जो कुछ अन्य चीजें जोड़ सकते हैं: 1. निर्माण पर बाइट [] की प्रतिलिपि लें, इसलिए यह गारंटी है कि ऑब्जेक्ट अपरिवर्तनीय है, जिसका अर्थ है कि समय के साथ आपकी कुंजी का हैश कोड बदल जाएगा। 2. एक बार हैश कोड को पूर्व-गणना और स्टोर करें (माना जाता है कि भंडारण ओवरहेड की तुलना में गति अधिक महत्वपूर्ण है)। – Adamski

+1

@Adamski: मैं उत्तर के अंत में कॉपी करने की संभावना का उल्लेख करता हूं। कुछ मामलों में यह करना सही बात है, लेकिन दूसरों में नहीं। मैं शायद इसे एक विकल्प बनाना चाहता हूं (संभावित रूप से रचनाकारों के बजाय स्थैतिक तरीकों - copyOf और wrapperAround)। ध्यान दें कि * बिना * प्रतिलिपि के, आप अंतर्निहित सरणी को तब तक बदल सकते हैं जब तक कि आप पहले हैश लेते हैं और समानता की जांच नहीं करते हैं, जो कुछ स्थितियों में उपयोगी हो सकता है। –

0

मैं समस्याओं को देखने के बाद आप Arrays.equals और Array.hashCode का उपयोग करना चाहिए, डिफ़ॉल्ट सरणी कार्यान्वयन के स्थान पर

+0

और आप हैश मैप को उन लोगों का उपयोग कैसे करेंगे? –

+0

जॉन स्कीट का उत्तर (एक बाइट सरणी रैपर) – dfa

0

Arrays.toString (बाइट)

+1

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

11

आप java.math.BigInteger इस्तेमाल कर सकते हैं। इसमें BigInteger(byte[] val) कन्स्ट्रक्टर है। यह एक संदर्भ प्रकार है, इसलिए हैशटेबल के लिए एक कुंजी के रूप में इस्तेमाल किया जा सकता है। और .equals() और .hashCode() को संबंधित पूर्णांक संख्याओं के रूप में परिभाषित किया गया है, जिसका अर्थ है कि बिगइंटर के पास बाइट [] सरणी के रूप में समरूपता के बराबर बराबर है।

+0

धन्यवाद, भयानक समाधान! –

+11

अवांछित लगता है, लेकिन यह गलत है, क्योंकि दो सरणी जो केवल शून्य तत्वों ('{0,100} 'और' {100} ') में भिन्न होती हैं, वही बिगइंटर – leonbloy

+0

अच्छा बिंदु @leonbloy देगी। एक वर्कअराउंड हो सकता है: इसमें कुछ निश्चित गैर-शून्य अग्रणी बाइट स्थिरता जोड़कर, लेकिन इसे BigInteger कन्स्ट्रक्टर के चारों ओर एक रैपर लिखने की आवश्यकता होगी और हमें वापस जॉन की प्रतिक्रिया में वापस कर देगा। –

34

हम इस के लिए उपयोग कर सकते हैं ByteBuffer

HashMap<ByteBuffer, byte[]> kvs = new HashMap<ByteBuffer, byte[]>(); 
byte[] k1 = new byte[]{1,2 ,3}; 
byte[] k2 = new byte[]{1,2 ,3}; 
byte[] val = new byte[]{12,23,43,4}; 

kvs.put(ByteBuffer.wrap(k1), val); 
System.out.println(kvs.containsKey(ByteBuffer.wrap(k2))); 

प्रिंट होगा

true 
+1

+1 (मुझे लगता है ...) – Nicholas

+3

यह ByteBuffer.wrap() के साथ ठीक काम करता है, लेकिन सावधान रहें यदि बाइटबफर की सामग्री को एक समग्र कुंजी बनाने के लिए कुछ पुट() कॉल का उपयोग करके बनाया गया है बाइट सरणी इस मामले में अंतिम डालने() कॉल को रिवाइंड() कॉल के बाद किया जाना चाहिए - अन्यथा बराबर() अंतर्निहित बाइट सरणी में अलग-अलग डेटा होने पर भी सत्य लौटाता है। – RenniePet

+0

यह एक अच्छा समाधान होगा, लेकिन यदि आप मानचित्र को क्रमबद्ध करना चाहते हैं (जैसे मेरे मामले में) तो आप इस दृष्टिकोण का उपयोग नहीं कर सकते हैं। –

0

तुम भी करने के लिए बाइट [] परिवर्तित कर सकते हैं (यह मूल रूप से एक तुलनित्र के साथ बाइट [] आवरण है) एक ' बेस 32 या बेस 64 का उपयोग करके सुरक्षित 'स्ट्रिंग, उदाहरण के लिए:

byte[] keyValue = new byte[] {…}; 
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue); 

बेशक उपर्युक्त, ली के कई रूप हैं ke:

String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue); 
1

आप ByteArrKey और अधिभार hashCode और बराबर तरीकों की तरह एक वर्ग somthing बनाने का उपयोग करना चाहिए उन दोनों के बीच अनुबंध याद है।

इससे आपको अधिक लचीलापन मिलेगा क्योंकि आप बाइट सरणी के अंत में संलग्न 0 प्रविष्टियों को छोड़ सकते हैं, विशेष रूप से यदि आप केवल कुछ भाग को अन्य बाइट बफर बनाते हैं।

इस तरह आप तय करेंगे कि दोनों वस्तुएं बराबर कैसे होनी चाहिए।

2

मुझे आश्चर्य है कि उत्तर सबसे सरल विकल्प को इंगित नहीं कर रहे हैं।

हां, हैश मैप का उपयोग करना संभव नहीं है, लेकिन कोई भी आपको वैकल्पिक रूप से सॉर्टेड मैप का उपयोग करने से रोकता है। एकमात्र चीज एक तुलनात्मक लिखना है जिसे सरणी की तुलना करने की आवश्यकता है। यह एक HashMap के रूप में के रूप में performant नहीं है, लेकिन अगर आप एक सरल विकल्प चाहते हैं, ये रहा है (यदि आप कार्यान्वयन छुपाना चाहते हैं आप मानचित्र के साथ SortedMap जगह ले सकता है):

private SortedMap<int[], String> testMap = new TreeMap<>(new ArrayComparator()); 

private class ArrayComparator implements Comparator<int[]> { 
    @Override 
    public int compare(int[] o1, int[] o2) { 
     int result = 0; 
     int maxLength = Math.max(o1.length, o2.length); 
     for (int index = 0; index < maxLength; index++) { 
     int o1Value = index < o1.length ? o1[index] : 0; 
     int o2Value = index < o2.length ? o2[index] : 0; 
     int cmp  = Integer.compare(o1Value, o2Value); 
     if (cmp != 0) { 
      result = cmp; 
      break; 
     } 
     } 
     return result; 
    } 
    } 

इस कार्यान्वयन दूसरे के लिए समायोजित किया जा सकता सरणी, केवल एक चीज जिसे आपको अवगत होना चाहिए वह यह है कि बराबर सरणी (बराबर सदस्यों के बराबर लंबाई) 0 को वापस करनी चाहिए और आपके पास एक दृढ़ संकल्प

0

यहां ट्रीमैप, तुलनात्मक इंटरफ़ेस और जावा विधि जावा का उपयोग करने का एक समाधान है। use.Arays.equals (बाइट [], बाइट []);

नोट: मानचित्र में आदेश देने के लिए इस विधि का

SortedMap<byte[], String> testMap = new TreeMap<>(new ArrayComparator()); 

static class ArrayComparator implements Comparator<byte[]> { 
    @Override 
    public int compare(byte[] byteArray1, byte[] byteArray2) { 

     int result = 0; 

     boolean areEquals = Arrays.equals(byteArray1, byteArray2); 

     if (!areEquals) { 
      result = -1; 
     } 

     return result; 
    } 
} 
संबंधित मुद्दे