इसका असली जवाब
क्यों एक Comparator
इंटरफ़ेस है, लेकिन नहीं Hasher
और Equator
?
जोश बलोच के सौजन्य से बोली :
मूल जावा एपीआई एक समापन बाजार की खिड़की को पूरा करने के लिए एक तंग समय सीमा के तहत बहुत जल्दी किया गया था। मूल जावा टीम ने एक अविश्वसनीय काम किया, लेकिन सभी एपीआई परिपूर्ण नहीं हैं।
यह समस्या पूरी तरह से जावा के इतिहास में निहित है, अन्य समान मामलों, जैसे .clone()
बनाम Cloneable
।
tl; डॉ
यह ऐतिहासिक कारणों से मुख्य रूप से है; वर्तमान व्यवहार / अमूर्तता को JDK 1.0 में पेश किया गया था और बाद में तय नहीं किया गया था क्योंकि पिछड़े कोड संगतता को बनाए रखने के साथ ऐसा करना लगभग असंभव था।
सबसे पहले, जाने-माने जावा तथ्यों के एक जोड़े को जोड़ते हैं:
- जावा, आज की शुरुआत से ही, गर्व से पीछे की ओर संगत था, विरासत एपीआई की आवश्यकता अभी भी नए संस्करणों में समर्थित है,
- जैसे, JDK 1.0 के साथ पेश की गई लगभग हर भाषा का निर्माण वर्तमान दिन तक रहता था,
Hashtable
, .hashCode()
और .equals()
JDK 1.0 में लागू किया गया, ( हैशटेबल )
Comparable
/ Comparator
JDK 1.2 ( तुलनीय ) में पेश किया गया था ,
अब, यह इस प्रकार है:
- वस्तुतः असंभव था
.hashCode()
और .equals()
पीछे हटने के लिए और अलग-अलग इंटरफेस के प्रति संवेदनहीन और संवेदनाहीन था, जबकि लोगों को एहसास होने के बाद भी पीछे संगतता बनाए रखना बेहतर था क्योंकि उन्हें सुपरबॉजेक्ट में डाल दिया गया था, क्योंकि 1.2 द्वारा प्रत्येक जावा प्रोग्रामर को पता था कि हर Object
कोई उनके पास है, और उनके पास था संकलित कोड (जेवीएम) संगतता प्रदान करने के लिए शारीरिक रूप से वहां रहने के लिए - और प्रत्येक Object
उपवर्ग में एक स्पष्ट इंटरफ़ेस जोड़ना जो वास्तव में उन्हें लागू करते हैं, इस गड़बड़ को समान (sic!) Clonable
एक के रूप में करेंगे ( बलोच चर्चा करता है कि क्लोन योग्य क्यों , उदाहरण के लिए EJ 2nd में भी चर्चा की गई है! और SO सहित कई अन्य स्थान),
- वे सिर्फ उन्हें भविष्य की पीढ़ी के लिए डब्ल्यूटीएफ के निरंतर स्रोत के लिए वहां छोड़ गए हैं।
अब, आप पूछ सकते हैं कि " Hashtable
इस सब के साथ क्या है "?
इसका उत्तर है: 1995/1996 में कोर जावा डेवलपर्स के अनुबंध hashCode()
/equals()
और न ही इतनी अच्छी भाषा डिजाइन कौशल।
जावा से भाषा 1.0 बोली , दिनांक 1996 - 4.3.2 द क्लास Object
, पृष्ठ 4:
हैशटैब जैसे (are21.7) के लाभ के लिए तरीके equals
और घोषणाएं की जाती हैं। विधि के बराबर वस्तु समानता की धारणा को परिभाषित करता है, जो मूल्य पर आधारित है, संदर्भ नहीं, तुलना।hashCode
java.util.Hashtable
(ध्यान दें कि इस सटीक कथन को बाद के संस्करणों में बदल दिया गया है , कहने के लिए, उद्धरण: ऐतिहासिक जेएलएस को पढ़े बिना The method hashCode is very useful, together with the method equals, in hashtables such as java.util.HashMap.
सीधे Hashtable
- hashCode
- equals
संबंध बनाना असंभव बना देता है !)
जावा टीम ने फैसला किया कि वे एक अच्छा शब्दकोश-शैली संग्रह चाहते हैं, और उन्होंने Hashtable
(अब तक अच्छा विचार) बनाया है, लेकिन वे चाहते थे कि प्रोग्रामर इसे कम से कम कोड / लर्निंग कर्व के रूप में उपयोग कर सकें (उफ़! आने वाली परेशानी!) - और, चूंकि अभी तक कोई जेनेरिक नहीं था [यह जेडीके 1.0 सब के बाद है], इसका मतलब यह होगा कि या तो हर Object
पुट में Hashtable
कुछ इंटरफ़ेस को स्पष्ट रूप से लागू करना होगा (और इंटरफेस अभी भी उनकी स्थापना में वापस थे ... फिर भी नहीं Comparable
!) , यह कई के लिए इसका इस्तेमाल करने के लिए एक निवारक बना रहा है - या कुछ हैशिंग विधि Object
को स्पष्ट रूप से लागू करना होगा ।
जाहिर है, वे समाधान 2 के साथ ऊपर उल्लिखित कारणों के लिए गए थे। हाँ, अब हम जानते हैं कि वे गलत थे। ... हंड्रेड में स्मार्ट होना आसान है। कुड़ाकुड़ाना
अब, hashCode()
आवश्यकता है कि प्रत्येक वस्तु के पास एक अलग equals()
विधि होनी चाहिए - इसलिए यह काफी स्पष्ट equals()
था कि इसे भी डाल दिया जाना था Object
।
के बाद से डिफ़ॉल्ट वैध पर उन तरीकों के कार्यान्वयन a
और b
Object
(बनाने रों अनिवार्य रूप से अनावश्यक होने से बेकार हैं a.equals(b)
बराबर करने के लिए a==b
और a.hashCode() == b.hashCode()
लगभग बराबर करने के लिए a==b
, यह भी जब तक hashCode
और / या equals
ओवरराइड नहीं है, या आप जीसी लाखों Object
अपने आवेदन के जीवन चक्र के दौरान रों 1 ) , यह कहना सुरक्षित है कि वे मुख्य रूप से एक बैकअप उपाय और उपयोग की सुविधा के लिए प्रदान किए गए थे। यह वास्तव में हमें ज्ञात तथ्य से मिलता है जो हमेशा दोनों को ओवरराइड करता है .equals()
और .hashCode()
यदि आप वास्तव में वस्तुओं की तुलना करना चाहते हैं या हैश-स्टोर कर रहे हैं। उनमें से केवल एक को दूसरे के बिना ओवरराइड करना आपके कोड को खराब करने का एक अच्छा तरीका है (दुष्ट परिणामों की तुलना करें या अत्यधिक उच्च बाल्टी टकराव के मूल्यों से) - और इसके चारों ओर अपना सिर प्राप्त करना शुरुआती लोगों के लिए निरंतर भ्रम और त्रुटियों का स्रोत है (खोज एसओ को देखने के लिए) अपने लिए) और अधिक अनुभवी लोगों के लिए निरंतर उपद्रव।
यह भी ध्यान दें, हालांकि C # समान तरीके से समान और हैशकोड के साथ काम करता है, एरिक लिपर्ट खुद कहते हैं कि उन्होंने C # के साथ लगभग वही गलती की जो Sun ने जावा के साथ C # स्थापना के पहले की थी :
लेकिन ऐसा क्यों होना चाहिए कि हर वस्तु को हैश टेबल में डालने के लिए खुद को हैश करने में सक्षम होना चाहिए? हर वस्तु को करने में सक्षम होने के लिए एक अजीब चीज की तरह लगता है। मुझे लगता है कि अगर हम आज स्क्रैच से टाइप सिस्टम को नया रूप दे रहे हैं, तो हैशिंग को अलग तरीके से किया जा सकता है, शायद एक IHashable
इंटरफेस के साथ । लेकिन जब सीएलआर प्रकार प्रणाली डिजाइन की गई थी तो कोई सामान्य प्रकार नहीं थे और इसलिए किसी भी वस्तु को संग्रहीत करने में सक्षम होने के लिए एक सामान्य-उद्देश्य वाली हैश तालिका की आवश्यकता थी।
1 बेशक, Object#hashCode
अभी भी टकरा सकता है, लेकिन इसे करने के लिए थोड़ा प्रयास करना पड़ता है, देखें: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6809470 और विवरणों से जुड़ी बग रिपोर्ट; /programming/1381060/hashcode-uniqueness/1381114#1381114 इस विषय को और अधिक गहराई से शामिल करता है।
Person
अपेक्षितequals
औरhashCode
व्यवहार को लागू करने के लिए रैपर वस्तुओं को पेश कर सकते हैं । तुम तो एक होगाHashMap<PersonWrapper, V>
। यह एक उदाहरण है जहां एक शुद्ध-ओओपी दृष्टिकोण सुरुचिपूर्ण नहीं है: किसी वस्तु पर प्रत्येक ऑपरेशन उस वस्तु की विधि के रूप में समझ में नहीं आता है। जावा के पूरेObject
प्रकार विभिन्न जिम्मेदारियों का एक मिश्रण है - केवलgetClass
,finalize
औरtoString
तरीकों आज के सर्वोत्तम प्रथाओं से दूर से न्यायोचित लगते हैं।