ओवरराइडिंग के लिए सर्वोत्तम प्रथाएँ इस प्रकार हैं: और हैश


267

isEqual:ऑब्जेक्टिव-सी में आप कैसे ठीक से ओवरराइड करते हैं ? "पकड़" ऐसा लगता है कि यदि दो वस्तुएं समान हैं (जैसा कि निर्धारित किया गया है)isEqual: विधि है), उनके पास समान हैश मान होना चाहिए।

आत्मनिरीक्षण की धारा कोको बुनियादी बातों गाइड कैसे ओवरराइड करने के लिए एक उदाहरण है isEqual:, इस प्रकार की नकल की, नाम के एक वर्ग के लिए MyWidget:

- (BOOL)isEqual:(id)other {
    if (other == self)
        return YES;
    if (!other || ![other isKindOfClass:[self class]])
        return NO;
    return [self isEqualToWidget:other];
}

- (BOOL)isEqualToWidget:(MyWidget *)aWidget {
    if (self == aWidget)
        return YES;
    if (![(id)[self name] isEqual:[aWidget name]])
        return NO;
    if (![[self data] isEqualToData:[aWidget data]])
        return NO;
    return YES;
}

यह सूचक समानता, फिर वर्ग समानता की जांच करता है, और अंत में उपयोग करने वाली वस्तुओं की तुलना isEqualToWidget:करता है, जो केवल nameऔर dataगुणों की जांच करता है । उदाहरण क्या नहीं दिखाता है कि कैसे ओवरराइड करना है hash

मान लीजिए कि अन्य गुण हैं जो समानता को प्रभावित नहीं करते हैं, कहते हैं age। क्या hashविधि को इस तरह से ओवरराइड नहीं किया जाना चाहिए nameऔर केवल dataहैश को प्रभावित करना चाहिए ? और यदि हां, तो आप ऐसा कैसे करेंगे? बस nameऔर की राख जोड़ें data? उदाहरण के लिए:

- (NSUInteger)hash {
    NSUInteger hash = 0;
    hash += [[self name] hash];
    hash += [[self data] hash];
    return hash;
}

क्या यह पर्याप्त है? क्या कोई बेहतर तकनीक है? क्या होगा यदि आपके पास आदिम हैं, जैसे int? उन्हें NSNumberअपने हैश पाने के लिए परिवर्तित करें ? या संरचना पसंद है NSRect?

( मस्तिष्क गोज़ : मूल रूप से "बिटवाइड या" उन्हें एक साथ लिखा |=। मीट एड।)


2
if (![other isKindOfClass:[self class]])- तकनीकी रूप से इसका मतलब समानता समानता नहीं होगी। Ie A = B का अर्थ B = A नहीं है (जैसे यदि एक दूसरे का उपवर्ग है)
रॉबर्ट

दस्तावेज़ लिंक मर चुका है, जो अब आत्मनिरीक्षण के लिए तैयार है
jedwidz

जवाबों:


111

के साथ शुरू

 NSUInteger prime = 31;
 NSUInteger result = 1;

फिर हर आदिम के लिए आप करते हैं

 result = prime * result + var

वस्तुओं के लिए आप शून्य के लिए 0 का उपयोग करते हैं और अन्यथा उनका हैशकोड।

 result = prime * result + [var hash];

बूलियन के लिए आप दो अलग-अलग मूल्यों का उपयोग करते हैं

 result = prime * result + ((var)?1231:1237);

स्पष्टीकरण और विशेषता

यह tcurdt का काम नहीं है, और टिप्पणियाँ अधिक स्पष्टीकरण के लिए पूछ रही थीं, इसलिए मेरा मानना ​​है कि एट्रिब्यूशन के लिए एक संपादन उचित है।

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

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


9
1231: 1237 कहां से आया? मैं इसे जावा के बूलियन.शशकोड () में भी देखता हूं। क्या यह जादुई है?
डेविड लियोनार्ड

17
यह हैशिंग एल्गोरिदम की प्रकृति है कि वहाँ टकराव होगा। इसलिए मैं आपकी बात नहीं देखता, पॉल।
tcurdt

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

6
पहली समस्या - (int) छोटी और अतिप्रवाह करने में आसान है, NSUInteger का उपयोग करें। दूसरी समस्या - यदि आप परिणाम को प्रत्येक चर हैश से गुणा करते रहेंगे तो आपका परिणाम समाप्त हो जाएगा। जैसे। [NSString हैश] बड़े मूल्य बनाता है। यदि आपके पास 5+ चर हैं तो इस एल्गोरिथ्म के साथ अतिप्रवाह करना आसान है। यह सब कुछ उसी हैश के लिए मैपिंग में परिणाम देगा, जो खराब है। मेरी प्रतिक्रिया देखें: stackoverflow.com/a/4393493/276626
पॉल सोल्ट

10
@PaulSolt - हैश पैदा करने में ओवरफ्लो कोई समस्या नहीं है, टक्कर है। लेकिन अतिप्रवाह आवश्यक रूप से टकराव की संभावना नहीं बनाता है, और अतिप्रवाह के बारे में आपका बयान सब कुछ उसी हैश में मैप करने का कारण है, बस गलत है।
डग डब्ल्यू

81

मैं सिर्फ ऑब्जेक्टिव-सी उठा रहा हूं, इसलिए मैं उस भाषा के लिए विशेष रूप से बात नहीं कर सकता, लेकिन अन्य भाषाओं में अगर मैं दो उदाहरण "समान" का उपयोग करता हूं तो उन्हें एक ही हैश वापस करना होगा - अन्यथा आप सभी के लिए जा रहे हैं एक हैशटेबल (या किसी भी शब्दकोश-प्रकार संग्रह) में कुंजी के रूप में उपयोग करने का प्रयास करते समय समस्याओं का प्रकार।

दूसरी ओर, यदि 2 उदाहरण समान नहीं हैं, तो उनके पास समान हैश नहीं हो सकता है या नहीं - यह सबसे अच्छा है यदि वे नहीं करते हैं। यह हैश टेबल पर O (1) खोज और O (N) खोज में अंतर है - यदि आपकी सभी हैश आपस में टकराती हैं, तो आप पा सकते हैं कि आपकी तालिका को खोजना किसी सूची को खोजने से बेहतर नहीं है।

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

वहाँ कई हैशिंग एल्गोरिदम हैं, जिनमें से कई यहां सूचीबद्ध हैं। मैं नए हैश एल्गोरिदम बनाने से बचने की कोशिश करता हूं क्योंकि इसमें बड़े प्रदर्शन निहितार्थ हो सकते हैं, इसलिए मौजूदा हैश विधियों का उपयोग करना और किसी प्रकार का एक बिटवाइज़ संयोजन करना जैसा कि आप अपने उदाहरण में करते हैं, इससे बचने का एक अच्छा तरीका है।


4
+1 उत्कृष्ट उत्तर, अधिक उत्थान के हकदार हैं, खासकर जब से वह वास्तव में "सर्वोत्तम प्रथाओं" के बारे में बात करते हैं और इसके पीछे सिद्धांत एक अच्छा (अद्वितीय) हैश क्यों महत्वपूर्ण है।
क्विन टेलर

30

महत्वपूर्ण गुणों के हैश मूल्यों पर एक साधारण XOR समय का पर्याप्त 99% है।

उदाहरण के लिए:

- (NSUInteger)hash
{
    return [self.name hash] ^ [self.data hash];
}

मैट थॉम्पसन द्वारा समाधान http://nshipster.com/equality/ पर पाया गया (जिसने अपनी पोस्ट में इस प्रश्न को भी संदर्भित किया!)


1
इस जवाब के साथ समस्या यह है कि यह आदिम मूल्यों पर बिल्कुल भी विचार नहीं करता है। और हैशिंग के लिए आदिम मूल्य भी महत्वपूर्ण हो सकते हैं।
विवे करें

@ इन समस्याओं में से अधिकांश स्विफ्ट में हल की जाती हैं, लेकिन ये प्रकार आमतौर पर अपने स्वयं के हैश का प्रतिनिधित्व करते हैं क्योंकि वे आदिम हैं।
यारिव निसिम

1
जबकि आप स्विफ्ट के लिए सही हैं, फिर भी वहाँ कई प्रोजेक्ट हैं जो objc के साथ लिखे गए हैं। क्योंकि आपका जवाब objc के लिए समर्पित है, यह कम से कम एक उल्लेख के लायक है।
विवे करें

एक साथ हैश मानों की सलाह देना बुरी सलाह है, इससे कई हैश टकराव होते हैं। इसके बजाय, एक अभाज्य संख्या के साथ गुणा करें और फिर अन्य उत्तर स्थिति के रूप में जोड़ें।
मछली मारना

27

मैंने पाया कि यह धागा मुझे अपना isEqual:और hashविधियों को एक कैच के साथ लागू करने के लिए आवश्यक हर चीज की आपूर्ति करने में मददगार है । isEqual:उदाहरण कोड में ऑब्जेक्ट उदाहरण चर का परीक्षण करते समय उपयोग करता है:

if (![(id)[self name] isEqual:[aWidget name]])
    return NO;

यह बार-बार विफल ( यानी , लौटाया नहीं ) बिना और त्रुटि के, जब मुझे पता था कि मेरे यूनिट परीक्षण में ऑब्जेक्ट समान थे। इसका कारण था, NSStringउदाहरण के चर में से एक शून्य था इसलिए उपरोक्त कथन था:

if (![nil isEqual: nil])
    return NO;

और चूंकि निल किसी भी विधि का जवाब देगा, यह पूरी तरह से कानूनी है लेकिन

[nil isEqual: nil]

रिटर्न एनआईएल , जो कि NO है , इसलिए जब ऑब्जेक्ट और टेस्ट किए गए दोनों में एक nil ऑब्जेक्ट होता है , तो उन्हें समान नहीं माना जाएगा ( यानी , NOisEqual: लौटेगा )।

यदि यह कथन बदलना था, तो यह सरल फिक्स था:

if ([self name] != [aWidget name] && ![(id)[self name] isEqual:[aWidget name]])
    return NO;

इस तरह, यदि उनके पते समान हैं तो यह विधि को रोक देता है, चाहे वे दोनों शून्य हों या दोनों एक ही वस्तु की ओर इशारा करते हों, लेकिन यदि दोनों शून्य नहीं हैं, तो या वे अलग-अलग वस्तुओं की तो तुलनित्र को उचित रूप से बुलाया जाता है।

मुझे आशा है कि यह किसी को सिर खुजाने के कुछ मिनट बचाता है।


20

हैश फ़ंक्शन को एक अर्ध-अद्वितीय मान बनाना चाहिए जो किसी अन्य ऑब्जेक्ट के हैश मान को टकराने या मेल खाने की संभावना नहीं है।

यहां पूरा हैश फ़ंक्शन है, जिसे आपकी कक्षाओं के उदाहरण चर के लिए अनुकूलित किया जा सकता है। यह 64 / 32bit अनुप्रयोगों पर संगतता के लिए NSUInteger के बजाय int का उपयोग करता है।

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

-(NSUInteger)hash {
    NSUInteger result = 1;
    NSUInteger prime = 31;
    NSUInteger yesPrime = 1231;
    NSUInteger noPrime = 1237;

    // Add any object that already has a hash function (NSString)
    result = prime * result + [self.myObject hash];

    // Add primitive variables (int)
    result = prime * result + self.primitiveVariable; 

    // Boolean values (BOOL)
    result = prime * result + (self.isSelected?yesPrime:noPrime);

    return result;
}

3
एक गोटचा यहां है: मैं डॉट सिंटैक्स से बचना पसंद करता हूं, इसलिए मैंने आपका BOOL स्टेटमेंट (जैसे) में बदल दिया result = prime * result + [self isSelected] ? yesPrime : noPrime;। फिर मैंने पाया कि यह result(उदाहरण के लिए) सेट कर 1231रहा था, मैं मानता हूं कि ?ऑपरेटर पूर्वता ले रहा है। मैंने कोष्ठक जोड़कर इस मुद्दे को तय किया:result = prime * result + ([self isSelected] ? yesPrime : noPrime);
एशले

12

आसान लेकिन अक्षम तरीका -hashहर उदाहरण के लिए समान मूल्य वापस करना है । अन्यथा, हां, आपको केवल उन वस्तुओं के आधार पर हैश को लागू करना चाहिए जो समानता को प्रभावित करते हैं। यह मुश्किल है अगर आप में लैक्स तुलना का उपयोग करते हैं-isEqual: (जैसे केस-असंवेदनशील स्ट्रिंग तुलना) । Ints के लिए, आप आम तौर पर स्वयं int का उपयोग कर सकते हैं, जब तक कि आप NSNumbers के साथ तुलना नहीं करेंगे।

उपयोग न करें | =, हालांकि, यह संतृप्त होगा। इसके बजाय ^ = का उपयोग करें।

यादृच्छिक मजेदार तथ्य: [[NSNumber numberWithInt:0] isEqual:[NSNumber numberWithBool:NO]]लेकिन [[NSNumber numberWithInt:0] hash] != [[NSNumber numberWithBool:NO] hash]। (rdar: // 4538282, 05 मई-2006 से खुला)


1
तुम बिल्कुल सही हो | = | वास्तव में इसका मतलब यह नहीं था। :) + = और ^ = काफी समतुल्य हैं। आप डबल और फ्लोट जैसे गैर-पूर्णांक प्राइमेटिव को कैसे संभालते हैं?
डेव ड्रिनिन

रैंडम मजेदार तथ्य: स्नो लेपर्ड पर इसका परीक्षण ... ;-)
क्विन टेलर

वह खेतों को एक हैश में संयोजित करने के बजाय XOR का उपयोग करने के बारे में सही है। हालांकि, हर वस्तु के लिए समान-मूल्य वापस करने की सलाह का उपयोग न करें - हालांकि यह आसान है, यह वस्तु के हैश का उपयोग करने वाली किसी भी चीज के प्रदर्शन को गंभीर रूप से कम कर सकता है । हैश को उन वस्तुओं के लिए अलग होना जरूरी नहीं है जो समान नहीं हैं, लेकिन यदि आप इसे प्राप्त कर सकते हैं, तो ऐसा कुछ भी नहीं है।
क्विन टेलर

खुली रडार बग रिपोर्ट बंद है। Openradar.me/4538282 इसका क्या मतलब है?
JJD

JJD, बग को मैक ओएस एक्स 10.6 में तय किया गया था, जैसा कि क्विन ने संकेत दिया था। (ध्यान दें कि टिप्पणी दो साल पुरानी है।)
जेन्स एटन

9

याद रखें कि आपको केवल हैश प्रदान करने की आवश्यकता है जो isEqualसच होने पर बराबर है। जब isEqualझूठ होता है, तो हैश को असमान नहीं होना पड़ता है, हालांकि संभवतः ऐसा है। अत:

हैश को सरल रखें। एक सदस्य (या कुछ सदस्य) चर चुनें जो सबसे विशिष्ट है।

उदाहरण के लिए, CLPlacemark के लिए, केवल नाम ही पर्याप्त है। हां, सटीक नाम के साथ 2 या 3 अलग-अलग CLPlacemark हैं, लेकिन वे दुर्लभ हैं। उस हैश का उपयोग करें।

@interface CLPlacemark (equal)
- (BOOL)isEqual:(CLPlacemark*)other;
@end

@implementation CLPlacemark (equal)

...

-(NSUInteger) hash
{
    return self.name.hash;
}


@end

ध्यान दें, मैं शहर, देश इत्यादि को निर्दिष्ट करने की जहमत नहीं उठाता। शायद नाम और CLLocation।

हैश समान रूप से वितरित किया जाना चाहिए। तो आप कैरेट ^ (xor साइन) का उपयोग करके कई सदस्यों के चर को जोड़ सकते हैं

तो यह कुछ ऐसा है

hash = self.member1.hash ^ self.member2.hash ^ self.member3.hash

इस तरह हैश समान रूप से वितरित किया जाएगा।

Hash must be O(1), and not O(n)

तो सरणी में क्या करना है?

फिर से, सरल। आपको सरणी के सभी सदस्यों को हैश नहीं करना है। पहले तत्व, अंतिम तत्व, गिनती, शायद कुछ मध्य तत्वों को हैश करने के लिए पर्याप्त है, और यह बात है।


XORing हैश मान वितरण भी नहीं देता है।
मत्स्य

7

इसे पकड़ें, निश्चित रूप से ऐसा करने का एक आसान तरीका यह है कि पहले - (NSString )descriptionअपने ऑब्जेक्ट स्टेट को ओवरराइड करें और एक स्ट्रिंग प्रतिनिधित्व प्रदान करें (आपको इस स्ट्रिंग में अपनी ऑब्जेक्ट की संपूर्ण स्थिति का प्रतिनिधित्व करना होगा)।

फिर, बस निम्नलिखित कार्यान्वयन प्रदान करें hash:

- (NSUInteger)hash {
    return [[self description] hash];
}

यह इस सिद्धांत पर आधारित है कि "यदि दो स्ट्रिंग ऑब्जेक्ट समान हैं (जैसा कि isEqualToString: विधि द्वारा निर्धारित किया गया है), तो उनके पास समान हैश मान होना चाहिए।"

स्रोत: एनएसएसट्रिंग क्लास संदर्भ


1
यह मानता है कि वर्णन विधि अद्वितीय होगी। विवरण के हैश का उपयोग एक निर्भरता बनाता है, जो स्पष्ट नहीं हो सकता है, और टकराव का एक उच्च जोखिम है।
पॉल सोल

1
+1 किया गया। यह एक शानदार विचार है। यदि आप डरते हैं कि विवरण टकराव का कारण बनते हैं, तो आप इसे ओवरराइड कर सकते हैं।
14:49 पर user4951

धन्यवाद जिम, मैं इनकार नहीं करूंगा कि यह थोड़ा हैक है, लेकिन यह किसी भी मामले में काम करेगा जो मैं सोच सकता हूं - और जैसा कि मैंने कहा, यह प्रदान करते हुए कि आप ओवरराइड करते हैं description, मैं नहीं देखता कि यह क्यों हीन है उच्च मतदान समाधानों में से कोई भी। सबसे गणितीय रूप से सुरुचिपूर्ण समाधान नहीं हो सकता है, लेकिन यह करना चाहिए। जैसा कि ब्रायन बी कहते हैं (इस बिंदु पर सबसे अधिक उत्तर दिया गया): "मैं नए हैश एल्गोरिदम बनाने से बचने की कोशिश करता हूं" - सहमत! - मैं तो बस ! hashNSString
जोनाथन एलिस

अपवित्र क्योंकि यह एक साफ विचार है। मैं इसका उपयोग नहीं करने जा रहा हूँ, क्योंकि मुझे अतिरिक्त NSString आवंटन का डर है।
करवाग

1
यह एक सामान्य समाधान नहीं है क्योंकि अधिकांश वर्गों descriptionमें सूचक पता शामिल है। तो यह एक ही वर्ग के दो अलग-अलग उदाहरण बनाते हैं जो अलग-अलग हैश के बराबर होते हैं, जो इस मूल धारणा का उल्लंघन करते हैं कि दो समान वस्तुओं में एक ही हैश होता है!
डायगो टी

5

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

ग्रहण जावा में इन विधियों को उत्पन्न करने का एक विश्वसनीय काम करता है, इसलिए यहाँ एक उदाहरण उदाहरण के लिए हाथ से उद्देश्य-सी में लिया गया है:

- (BOOL)isEqual:(id)object {
    if (self == object)
        return true;
    if ([self class] != [object class])
        return false;
    MyWidget *other = (MyWidget *)object;
    if (_name == nil) {
        if (other->_name != nil)
            return false;
    }
    else if (![_name isEqual:other->_name])
        return false;
    if (_data == nil) {
        if (other->_data != nil)
            return false;
    }
    else if (![_data isEqual:other->_data])
        return false;
    return true;
}

- (NSUInteger)hash {
    const NSUInteger prime = 31;
    NSUInteger result = 1;
    result = prime * result + [_name hash];
    result = prime * result + [_data hash];
    return result;
}

और एक उपवर्ग के लिए YourWidgetजो एक संपत्ति जोड़ता हैserialNo :

- (BOOL)isEqual:(id)object {
    if (self == object)
        return true;
    if (![super isEqual:object])
        return false;
    if ([self class] != [object class])
        return false;
    YourWidget *other = (YourWidget *)object;
    if (_serialNo == nil) {
        if (other->_serialNo != nil)
            return false;
    }
    else if (![_serialNo isEqual:other->_serialNo])
        return false;
    return true;
}

- (NSUInteger)hash {
    const NSUInteger prime = 31;
    NSUInteger result = [super hash];
    result = prime * result + [_serialNo hash];
    return result;
}

यह कार्यान्वयन नमूने में कुछ उपवर्गीय नुकसान से बचा जाता है isEqual: एप्पल से :

  • एप्पल के वर्ग परीक्षण other isKindOfClass:[self class]दो अलग-अलग उपवर्गों के लिए असममित है MyWidget। समानता को सममित होना चाहिए: a = b यदि और केवल यदि b = a। यह आसानी से परीक्षण को बदलकर तय किया जा सकता हैother isKindOfClass:[MyWidget class] , फिर सभी MyWidgetउपवर्ग परस्पर तुलनात्मक होंगे।
  • isKindOfClass:उपवर्ग परीक्षण का उपयोग करना उपवर्गों को isEqual:परिष्कृत समानता परीक्षण के साथ ओवरराइड करने से रोकता है । ऐसा इसलिए है क्योंकि समानता को सकर्मक होना चाहिए: यदि a = b और a = c तो b = c। यदि एक MyWidgetउदाहरण दो YourWidgetउदाहरणों के बराबर तुलना करता है , तो वेYourWidget उदाहरणों को एक दूसरे के बराबर तुलना करना चाहिए, भले ही उनका serialNoअंतर अलग हो।

दूसरा मुद्दा केवल वस्तुओं के बराबर होने पर विचार करके तय किया जा सकता है यदि वे ठीक उसी वर्ग के हैं, इसलिए [self class] != [object class] यहां परीक्षण। विशिष्ट एप्लिकेशन कक्षाओं के लिए , यह सबसे अच्छा तरीका है।

हालांकि, निश्चित रूप से ऐसे मामले हैं जहां isKindOfClass:परीक्षण बेहतर है। यह आवेदन कक्षाओं की तुलना में फ्रेमवर्क कक्षाओं का अधिक विशिष्ट है । उदाहरण के लिए, किसी NSStringको भी NSStringसमान अंतर्निहित चरित्र अनुक्रम के साथ किसी भी अन्य के बराबर की तुलना करनी चाहिए , भले ही NSString/ NSMutableStringअंतर के बिना, और यह भी परवाह किए बिना कि NSStringक्लास क्लस्टर में कौन सी निजी कक्षाएं शामिल हैं।

ऐसे मामलों में, isEqual:अच्छी तरह से परिभाषित, अच्छी तरह से प्रलेखित व्यवहार होना चाहिए, और यह स्पष्ट किया जाना चाहिए कि उपवर्ग इस से आगे नहीं बढ़ सकते हैं। जावा में, 'नो ओवरराइड' प्रतिबंध को समान और हैशकोड विधियों को चिह्नित करके लागू किया जा सकता है final, लेकिन ऑब्जेक्टिव-सी का कोई समकक्ष नहीं है।


@adubr यह मेरे पिछले दो पैराग्राफ में शामिल है। यह MyWidgetएक वर्ग क्लस्टर नहीं समझा जाता है के रूप में फोकल नहीं है।
jedwidz

5

यह सीधे आपके सवाल का जवाब नहीं देता (सब पर) लेकिन मैंने हश्र उत्पन्न करने से पहले मुरमुरैश का इस्तेमाल किया है: मुरमुरैश

मुझे लगता है कि क्यों समझा जाना चाहिए: बड़बड़ाहट तेजी से खूनी है ...


2
एक सी ++ लाइब्रेरी जो रैंडम नंबर (और ऑब्जेक्टिव-सी ऑब्जेक्ट्स से संबंधित नहीं है) का उपयोग करके एक शून्य * कुंजी के लिए अद्वितीय हैश पर केंद्रित है, वास्तव में यहाँ एक उपयोगी सुझाव नहीं है। -हाश पद्धति को हर बार एक सुसंगत मूल्य वापस करना चाहिए, या यह पूरी तरह से बेकार हो जाएगा। यदि ऑब्जेक्ट को संग्रह में जोड़ा जाता है, जो कॉल -हाश करता है, और हर बार एक नया मान देता है, तो डुप्लिकेट का कभी भी पता नहीं लगाया जाएगा, और आप संग्रह से ऑब्जेक्ट को कभी भी पुनर्प्राप्त नहीं कर सकते। इस मामले में, शब्द "हैश" सुरक्षा / क्रिप्टोग्राफी में अर्थ से अलग है।
क्विन टेलर

3
बड़बड़ाहट एक क्रिप्टोग्राफिक हैश फ़ंक्शन नहीं है। गलत जानकारी पोस्ट करने से पहले कृपया अपने तथ्यों की जाँच करें। मुरमुरश हैशिंग कस्टम उद्देश्य-सी वर्गों (esp के लिए उपयोगी हो सकता है। यदि आपके पास बहुत सारे NSDatas शामिल हैं) क्योंकि यह बहुत तेज़ है। हालाँकि मैं आपको यह अनुदान देता हूं कि शायद यह सुझाव देना किसी को "सिर्फ उद्देश्य-सी लेने" के लिए सबसे अच्छी सलाह नहीं है, लेकिन कृपया प्रश्न के मूल उत्तर पर मेरे उपसर्ग पर ध्यान दें।
schwa

5

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



4

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


मैं कोको / ऑब्जेक्टिव सी नौसिखिया हूं, और इस जवाब और लिंक ने मुझे वास्तव में नीचे पंक्ति के ऊपर सभी अधिक उन्नत सामानों के माध्यम से कटौती करने में मदद की - मुझे हैश के बारे में चिंता करने की ज़रूरत नहीं है - बस isEqual को लागू करना: विधि। धन्यवाद!
जॉन गैलाघर

@ Ceperry के लिंक को मिस न करें। Equality vs Identityकार्ल क्राफ्ट का लेख वास्तव में अच्छा है।
JJD

6
@ जॉन: मुझे लगता है कि आपको लेख को फिर से पढ़ना चाहिए। यह बहुत स्पष्ट रूप से कहता है कि "उदाहरण जो समान हैं उनके पास समान हैश मूल्य होना चाहिए।" यदि आप ओवरराइड करते हैं isEqual:, तो आपको ओवरराइड भी करना चाहिए hash
स्टीव मैडसेन

3

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

यहाँ कुछ प्रमुख बिंदु:

Tcurdt से उदाहरण फ़ंक्शन बताता है कि '31' एक अच्छा गुणक है क्योंकि यह प्रमुख है। यह दिखाने की जरूरत है कि प्रधान होना एक आवश्यक और पर्याप्त शर्त है। वास्तव में 31 (और 7) शायद विशेष रूप से अच्छे अपराध नहीं हैं क्योंकि 31 == -1% 32. एक विषम गुणक जिसमें लगभग आधे बिट सेट होते हैं और आधे बिट स्पष्ट होते हैं, बेहतर होने की संभावना है। (मुरम हैश गुणन स्थिरांक में वह गुण होता है।)

इस प्रकार के हैश फ़ंक्शन के मजबूत होने की संभावना होगी, यदि गुणा करने के बाद, परिणाम मान को एक शिफ्ट और xor के माध्यम से समायोजित किया गया था। गुणन रजिस्टर के उच्च अंत में बिट इंटरैक्शन के बहुत सारे परिणाम उत्पन्न करता है और रजिस्टर के निचले छोर पर कम इंटरैक्शन परिणाम देता है। शिफ्ट और एक्सर रजिस्टर के निचले छोर पर बातचीत को बढ़ाता है।

प्रारंभिक परिणाम को उस मूल्य पर सेट करना जहां लगभग आधे बिट्स शून्य हैं और लगभग आधे बिट्स हैं, जो उपयोगी होंगे।

तत्वों को संयोजित करने के क्रम में सावधानी बरतना उपयोगी हो सकता है। एक को पहले बूलियन और अन्य तत्वों को संसाधित करना चाहिए जहां मान दृढ़ता से वितरित नहीं किए जाते हैं।

गणना के अंत में अतिरिक्त बिट स्क्रैचिंग चरणों के एक जोड़े को जोड़ना उपयोगी हो सकता है।

इस आवेदन के लिए बड़बड़ाहट हैश वास्तव में तेज है या नहीं यह एक खुला प्रश्न है। बड़बड़ाहट हैश प्रत्येक इनपुट शब्द के बिट्स premixes। एकाधिक इनपुट शब्दों को समानांतर में संसाधित किया जा सकता है जो बहु-अंक पाइपलाइन किए गए cpus में मदद करता है।


3

संपत्ति के नाम प्राप्त करने के लिए @ ऑस्कर-गोमेज़ के उत्तर के साथ @ tcurdt के उत्तर को मिलाकर , हम ईक्वाल और हैश दोनों के लिए एक आसान ड्रॉप-इन समाधान बना सकते हैं:

NSArray *PropertyNamesFromObject(id object)
{
    unsigned int propertyCount = 0;
    objc_property_t * properties = class_copyPropertyList([object class], &propertyCount);
    NSMutableArray *propertyNames = [NSMutableArray arrayWithCapacity:propertyCount];

    for (unsigned int i = 0; i < propertyCount; ++i) {
        objc_property_t property = properties[i];
        const char * name = property_getName(property);
        NSString *propertyName = [NSString stringWithUTF8String:name];
        [propertyNames addObject:propertyName];
    }
    free(properties);
    return propertyNames;
}

BOOL IsEqualObjects(id object1, id object2)
{
    if (object1 == object2)
        return YES;
    if (!object1 || ![object2 isKindOfClass:[object1 class]])
        return NO;

    NSArray *propertyNames = PropertyNamesFromObject(object1);
    for (NSString *propertyName in propertyNames) {
        if (([object1 valueForKey:propertyName] != [object2 valueForKey:propertyName])
            && (![[object1 valueForKey:propertyName] isEqual:[object2 valueForKey:propertyName]])) return NO;
    }

    return YES;
}

NSUInteger MagicHash(id object)
{
    NSUInteger prime = 31;
    NSUInteger result = 1;

    NSArray *propertyNames = PropertyNamesFromObject(object);

    for (NSString *propertyName in propertyNames) {
        id value = [object valueForKey:propertyName];
        result = prime * result + [value hash];
    }

    return result;
}

अब, अपने कस्टम वर्ग में आप आसानी से लागू कर सकते हैं isEqual:और hash:

- (NSUInteger)hash
{
    return MagicHash(self);
}

- (BOOL)isEqual:(id)other
{
    return IsEqualObjects(self, other);
}

2

ध्यान दें कि यदि आप एक ऐसी वस्तु बना रहे हैं जिसे निर्माण के बाद म्यूट किया जा सकता है, तो वस्तु को एक संग्रह में सम्मिलित करने पर हैश मान नहीं बदलना चाहिए । व्यावहारिक रूप से, इसका अर्थ है कि हैश मान प्रारंभिक ऑब्जेक्ट निर्माण के बिंदु से तय किया जाना चाहिए। देखें NSObject प्रोटोकॉल के -hash पद्धति पर एप्पल के प्रलेखन अधिक जानकारी के लिए:

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

यह मुझे पूरी तरह से अजीब लगता है क्योंकि यह संभावित रूप से प्रभावी ढंग से हैश लुकअप को प्रस्तुत करता है, लेकिन मुझे लगता है कि यह सावधानी की ओर गलत है और प्रलेखन का कहना है कि पालन करें।


1
आप हैश डॉक्स गलत पढ़ रहे हैं - यह अनिवार्य रूप से एक "या तो या" स्थिति है। यदि वस्तु बदलती है, तो हैश आम तौर पर भी बदल जाता है। यह वास्तव में प्रोग्रामर के लिए एक चेतावनी है, कि यदि किसी वस्तु को उत्परिवर्तित करने के परिणामस्वरूप हैश बदलता है, तो ऑब्जेक्ट को बदलते समय यह संग्रह में रहता है जो हैश का उपयोग करता है, अप्रत्याशित व्यवहार का कारण होगा। यदि ऐसी स्थिति में ऑब्जेक्ट "सुरक्षित रूप से उत्परिवर्तित" होना चाहिए, तो आपके पास पारस्परिक स्थिति से हैश को असंबंधित करने के अलावा कोई विकल्प नहीं है। वह विशेष स्थिति मुझे अजीब लगती है, लेकिन जहाँ यह लागू होता है वहाँ निश्चित रूप से विषम परिस्थितियाँ होती हैं।
क्विन टेलर

1

क्षमा करें, यदि मुझे यहां पूर्ण रूप से बोफिन लगने का खतरा है, लेकिन ... किसी ने यह उल्लेख करने की जहमत नहीं उठाई कि 'सर्वोत्तम प्रथाओं' का पालन करने के लिए आपको निश्चित रूप से एक समान पद्धति को निर्दिष्ट नहीं करना चाहिए जो आपके लक्ष्य ऑब्जेक्ट के स्वामित्व वाले सभी डेटा को ध्यान में नहीं रखेगा, जैसे कि जो भी हो डेटा को आपकी वस्तु, बनाम इसके सहयोगी के रूप में एकत्र किया जाता है, जब समान लागू होता है, तो इसे ध्यान में रखा जाना चाहिए। यदि आप एक तुलना में खाते में 'आयु' कहना नहीं चाहते हैं, तो आपको एक तुलनित्र लिखना चाहिए और उपयोग करना चाहिए जो कि आपकी तुलना में isEqual के बजाय करें।

यदि आप एक आइसल परिभाषित करते हैं: विधि जो समानता की तुलना मनमाने ढंग से करती है, तो आप जोखिम उठाते हैं कि इस पद्धति का दूसरे डेवलपर द्वारा दुरुपयोग किया जाता है, या यहां तक ​​कि खुद भी, एक बार जब आप अपनी बराबर व्याख्या में 'मोड़' भूल गए हैं।

एर्गो, हालांकि यह हैशिंग के बारे में एक महान क्यू एंड है, आपको आमतौर पर हैशिंग विधि को फिर से परिभाषित करने की आवश्यकता नहीं है, आपको संभवतः इसके बजाय एक तदर्थ तुलनित्र को परिभाषित करना चाहिए।

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