ओवरराइड करते समय equals
और किन मुद्दों पर विचार किया जाना चाहिए hashCode
?
ओवरराइड करते समय equals
और किन मुद्दों पर विचार किया जाना चाहिए hashCode
?
जवाबों:
equals()
( javadoc ) को एक समतुल्य संबंध को परिभाषित करना चाहिए (यह प्रतिवर्त , सममित और परिवर्तनशील होना चाहिए )। इसके अलावा, यह सुसंगत होना चाहिए (यदि वस्तुओं को संशोधित नहीं किया गया है, तो इसे उसी मूल्य पर वापस रखना होगा)। इसके अलावा, o.equals(null)
हमेशा झूठी वापसी करनी चाहिए।
hashCode()
( javadoc ) भी सुसंगत होना चाहिए (यदि वस्तु को संदर्भ में संशोधित नहीं किया गया है equals()
, तो उसे उसी मान को लौटाते रहना चाहिए)।
संबंध दो तरीकों के बीच है:
जब भी
a.equals(b)
, तबa.hashCode()
जैसा होना चाहिएb.hashCode()
।
यदि आप एक को ओवरराइड करते हैं, तो आपको दूसरे को ओवरराइड करना चाहिए।
खेतों के उसी सेट का उपयोग करें जिसका उपयोग आप गणना करने के लिए करते हैं equals()
लिएhashCode()
।
अपाचे कॉमन्स लैंग लाइब्रेरी से उत्कृष्ट हेल्पर क्लास इक्वाल्सबर्ल और हैशकोडब्यूरीट का उपयोग करें । एक उदाहरण:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
जब का उपयोग कर एक हैश आधारित संग्रह या मानचित्र जैसे HashSet , LinkedHashSet , HashMap , Hashtable , या WeakHashMap , सुनिश्चित करें कि hashCode () कुंजी वस्तुओं की है कि आप संग्रह में कभी नहीं डाल जबकि वस्तु संग्रह में है परिवर्तन करें। यह सुनिश्चित करने का बुलेटप्रूफ तरीका आपकी चाबी को अपरिवर्तनीय बनाना है, जिसके अन्य लाभ भी हैं ।
instanceof
यदि इसका पहला ऑपरेंड शून्य है (तो प्रभावी जावा फिर से)।
यदि आप उन कक्षाओं के साथ व्यवहार कर रहे हैं जो हाइबरनेट की तरह एक ऑब्जेक्ट-रिलेशनशिप मैपर (ORM) का उपयोग कर रहे हैं, तो ध्यान देने योग्य कुछ मुद्दे हैं, अगर आपको नहीं लगता कि यह अनुचित रूप से पहले से ही जटिल था!
आलसी भरी वस्तुएँ उपवर्ग हैं
यदि आपकी वस्तुओं को ORM का उपयोग करके जारी रखा जाता है, तो कई मामलों में आप डेटा स्टोर से बहुत जल्दी ऑब्जेक्ट लोड करने से बचने के लिए डायनेमिक प्रॉक्सी से निपटेंगे। ये परदे के पीछे आपकी ही कक्षा के उपवर्ग के रूप में लागू किए जाते हैं। इसका मतलब है कि this.getClass() == o.getClass()
वापस आएगा false
। उदाहरण के लिए:
Person saved = new Person("John Doe");
Long key = dao.save(saved);
dao.flush();
Person retrieved = dao.retrieve(key);
saved.getClass().equals(retrieved.getClass()); // Will return false if Person is loaded lazy
यदि आप एक ORM के साथ काम कर रहे हैं, तो प्रयोग o instanceof Person
केवल एक चीज है जो सही ढंग से व्यवहार करेगी।
आलसी भरी वस्तुओं में अशक्त क्षेत्र होते हैं
ORM आमतौर पर आलसी भरी वस्तुओं को लोड करने के लिए गेटर्स का उपयोग करते हैं। इसका मतलब है कि person.name
हो जाएगा null
अगर person
, आलसी लोड है, भले ही person.getName()
बलों लदान और रिटर्न "जॉन डो"। मेरे अनुभव में, यह फसल अक्सर hashCode()
और अधिक होती है equals()
।
यदि आप एक ORM साथ हैं निपटने, हमेशा में ही टिककर खेल, और कभी क्षेत्र संदर्भ का उपयोग सुनिश्चित करें hashCode()
और equals()
।
किसी वस्तु को सहेजने से उसकी स्थिति बदल जाएगी
लगातार ऑब्जेक्ट्स अक्सर ऑब्जेक्ट id
की कुंजी को रखने के लिए एक फ़ील्ड का उपयोग करते हैं । जब कोई ऑब्जेक्ट पहली बार सहेजा जाता है तो यह फ़ील्ड अपने आप अपडेट हो जाएगी। किसी आईडी फ़ील्ड का उपयोग न करें hashCode()
। लेकिन आप इसमें इस्तेमाल कर सकते हैं equals()
।
एक पैटर्न जिसका मैं अक्सर उपयोग करता हूं
if (this.getId() == null) {
return this == other;
}
else {
return this.getId().equals(other.getId());
}
लेकिन: यदि आप शामिल नहीं कर सकते getId()
में hashCode()
। यदि आप करते हैं, जब कोई वस्तु बनी रहती है, तो उसके hashCode
परिवर्तन। यदि ऑब्जेक्ट एक में है HashSet
, तो आप इसे फिर कभी नहीं ढूंढेंगे।
मेरे Person
उदाहरण में, मैं शायद के getName()
लिए hashCode
और getId()
प्लस getName()
(सिर्फ व्यामोह के लिए) का उपयोग करूंगा equals()
। यह ठीक है अगर इसके लिए "टकराव" के कुछ जोखिम हैं hashCode()
, लेकिन इसके लिए कभी ठीक नहीं है equals()
।
hashCode()
से गुणों के गैर-बदलते सबसेट का उपयोग करना चाहिए equals()
Saving an object will change it's state
! hashCode
लौटना चाहिए int
, तो आप कैसे उपयोग करेंगे getName()
? आप अपने लिए एक उदाहरण दे सकते हैंhashCode
के बारे में एक स्पष्टीकरण obj.getClass() != getClass()
।
यह कथन equals()
अमित्रत्व के उत्तराधिकार होने का परिणाम है । जेएलएस (जावा लैंग्वेज स्पेसिफिकेशन) निर्दिष्ट करता है कि यदि आवश्यक हो A.equals(B) == true
तो B.equals(A)
वापस भी आना चाहिए true
। यदि आप उस कथन को छोड़ देते हैं, जो उन वर्गों को विरासत में देता है जो equals()
(और उसके व्यवहार को बदल देते हैं) इस विनिर्देश को तोड़ देंगे।
कथन के छोड़े जाने पर क्या होता है, इसके निम्नलिखित उदाहरण पर विचार करें:
class A {
int field1;
A(int field1) {
this.field1 = field1;
}
public boolean equals(Object other) {
return (other != null && other instanceof A && ((A) other).field1 == field1);
}
}
class B extends A {
int field2;
B(int field1, int field2) {
super(field1);
this.field2 = field2;
}
public boolean equals(Object other) {
return (other != null && other instanceof B && ((B)other).field2 == field2 && super.equals(other));
}
}
ऐसा करने से new A(1).equals(new A(1))
इसके अलावा, new B(1,1).equals(new B(1,1))
सच बाहर देते हैं, एकदम सही ढंग से उत्पन्न होती हैं।
यह सब बहुत अच्छा लग रहा है, लेकिन देखो कि क्या होता है अगर हम दोनों वर्गों का उपयोग करने की कोशिश करते हैं:
A a = new A(1);
B b = new B(1,1);
a.equals(b) == true;
b.equals(a) == false;
जाहिर है, यह गलत है।
यदि आप सममित स्थिति सुनिश्चित करना चाहते हैं। a = b यदि b = a और लिस्कोव प्रतिस्थापन सिद्धांत super.equals(other)
न केवल B
उदाहरण के मामले में कहता है , बल्कि उदाहरण के लिए जाँच करें A
:
if (other instanceof B )
return (other != null && ((B)other).field2 == field2 && super.equals(other));
if (other instanceof A) return super.equals(other);
else return false;
जो आउटपुट देगा:
a.equals(b) == true;
b.equals(a) == true;
जहां, यदि a
इसका संदर्भ नहीं है B
, तो यह वर्ग का संदर्भ हो सकता है A
(क्योंकि आप इसे बढ़ाते हैं), इस मामले में आप super.equals()
भी कॉल करते हैं ।
ThingWithOptionSetA
बराबर हो सकता है Thing
कि सभी अतिरिक्त विकल्पों में डिफ़ॉल्ट मान हैं, और इसी तरह से ThingWithOptionSetB
, तो यह संभव होना चाहिए ThingWithOptionSetA
कि इसकी तुलना ThingWithOptionSetB
केवल एक ही के बराबर की जाए यदि दोनों वस्तुओं के सभी गैर-आधार गुण उनकी चूक से मेल खाते हों, लेकिन मैं नहीं देखता कि आप उसके लिए कैसे परीक्षण करते हैं।
B b2 = new B(1,99)
, तो b.equals(a) == true
और a.equals(b2) == true
लेकिन b.equals(b2) == false
।
एक विरासत के अनुकूल कार्यान्वयन के लिए, ताल कोहेन के समाधान की जांच करें , मैं सही तरीके से बराबर () पद्धति को कैसे लागू करूं?
सारांश:
अपनी पुस्तक में प्रभावी जावा प्रोग्रामिंग लैंग्वेज गाइड (एडिसन-वेस्ले, 2001) में, जोशुआ बलोच का दावा है कि "एक तत्काल वर्ग का विस्तार करने और समान अनुबंध को संरक्षित करते हुए एक पहलू जोड़ने का कोई तरीका नहीं है।" ताल असहमत।
उसका समाधान एक और निरर्थक नेत्रहीन रूप से (दोनों) तरीके से कॉल करके समान () लागू करना है। आँख बंद करके () उपवर्गों द्वारा ओवरराइड किया जाता है, बराबर () विरासत में मिला है, और कभी भी ओवरराइड नहीं किया गया है।
उदाहरण:
class Point {
private int x;
private int y;
protected boolean blindlyEquals(Object o) {
if (!(o instanceof Point))
return false;
Point p = (Point)o;
return (p.x == this.x && p.y == this.y);
}
public boolean equals(Object o) {
return (this.blindlyEquals(o) && o.blindlyEquals(this));
}
}
class ColorPoint extends Point {
private Color c;
protected boolean blindlyEquals(Object o) {
if (!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint)o;
return (super.blindlyEquals(cp) &&
cp.color == this.color);
}
}
ध्यान दें कि बराबर () को वंशानुगत पदानुक्रम में काम करना होगा यदि लिस्कोव प्रतिस्थापन सिद्धांत को संतुष्ट करना है।
if (this.getClass() != o.getClass()) return false
, लेकिन इसमें लचीला है कि यह केवल तभी वापस आता है जब व्युत्पन्न वर्ग (तों) समान को संशोधित करने के लिए परेशान करता है। क्या वह सही है?
अभी भी आश्चर्यचकित है कि इसके लिए अमरूद पुस्तकालय की सिफारिश किसी ने नहीं की।
//Sample taken from a current working project of mine just to illustrate the idea
@Override
public int hashCode(){
return Objects.hashCode(this.getDate(), this.datePattern);
}
@Override
public boolean equals(Object obj){
if ( ! obj instanceof DateAndPattern ) {
return false;
}
return Objects.equal(((DateAndPattern)obj).getDate(), this.getDate())
&& Objects.equal(((DateAndPattern)obj).getDate(), this.getDatePattern());
}
this
में this.getDate()
साधन कुछ भी नहीं (अव्यवस्था के अलावा अन्य)
if (!(otherObject instanceof DateAndPattern)) {
:। हेरन और स्टीव कू के साथ सहमत (हालांकि वह व्यक्तिगत पसंद की बात है), लेकिन फिर भी +1।
सुपर क्लास में java.lang.Object के रूप में दो तरीके हैं। हमें उन्हें कस्टम ऑब्जेक्ट पर ओवरराइड करने की आवश्यकता है।
public boolean equals(Object obj)
public int hashCode()
समान वस्तुओं को समान हैश कोड का उत्पादन करना चाहिए जब तक वे समान हैं, हालांकि असमान वस्तुओं को अलग-अलग हैश कोड का उत्पादन करने की आवश्यकता नहीं है।
public class Test
{
private int num;
private String data;
public boolean equals(Object obj)
{
if(this == obj)
return true;
if((obj == null) || (obj.getClass() != this.getClass()))
return false;
// object must be Test at this point
Test test = (Test)obj;
return num == test.num &&
(data == test.data || (data != null && data.equals(test.data)));
}
public int hashCode()
{
int hash = 7;
hash = 31 * hash + num;
hash = 31 * hash + (null == data ? 0 : data.hashCode());
return hash;
}
// other methods
}
यदि आप अधिक प्राप्त करना चाहते हैं, तो कृपया इस लिंक को http://www.javaranch.com/journal/2002/10/equalhash.html देखें
यह एक और उदाहरण है, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
मज़े करो! @। @
सदस्य समानता की जाँच करने से पहले वर्ग समानता के लिए अपनी जाँच करने के कुछ तरीके हैं, और मुझे लगता है कि दोनों सही परिस्थितियों में उपयोगी हैं।
instanceof
ऑपरेटर का उपयोग करें ।this.getClass().equals(that.getClass())
।मैं एक final
समान कार्यान्वयन में # 1 का उपयोग करता हूं , या एक इंटरफ़ेस लागू करते समय जो समान के लिए एक एल्गोरिथ्म को निर्धारित करता है (जैसे java.util
संग्रह इंटरफेस - के साथ जांच करने का सही तरीका(obj instanceof Set)
आपके या जो भी इंटरफ़ेस आप लागू कर रहे हैं उसके )। यह आम तौर पर एक बुरा विकल्प होता है जब बराबरी पर रोक लगाई जा सकती है क्योंकि यह सममिति संपत्ति को तोड़ता है।
विकल्प # 2 वर्ग को समतुल्यता या ब्रेकिंग समरूपता के बिना सुरक्षित रूप से विस्तारित करने की अनुमति देता है।
यदि आपकी कक्षा भी है Comparable
, equals
और compareTo
विधियाँ भी सुसंगत होनी चाहिए। यहां Comparable
कक्षा में समान पद्धति के लिए एक टेम्पलेट दिया गया है :
final class MyClass implements Comparable<MyClass>
{
…
@Override
public boolean equals(Object obj)
{
/* If compareTo and equals aren't final, we should check with getClass instead. */
if (!(obj instanceof MyClass))
return false;
return compareTo((MyClass) obj) == 0;
}
}
final
, और compareTo()
विधि को क्रम क्रम को उलटने के लिए ओवरराइड किया गया था, तो उपवर्ग और सुपरक्लास के उदाहरणों को समान नहीं माना जाना चाहिए। जब इन वस्तुओं को एक पेड़ में एक साथ इस्तेमाल किया गया था, तो instanceof
कार्यान्वयन के अनुसार "बराबर" होने वाली चाबियां खोजने योग्य नहीं हो सकती हैं।
बराबरी के लिए, इस पर गौर बराबर का राज द्वारा एंजेलिका लैंगर । मुझे इससे बहुत प्यार है। वह जावा में जेनरिक के बारे में एक बहुत अच्छा प्रश्न है । यहां उसके अन्य लेख देखें (नीचे स्क्रॉल करें "कोर जावा"), जहां वह भाग -2 और "मिश्रित प्रकार की तुलना" भी करती है। उन्हें पढ़कर मजा आया!
बराबर () विधि का उपयोग दो वस्तुओं की समानता को निर्धारित करने के लिए किया जाता है।
10 के बराबर मान हमेशा 10 के बराबर होता है। लेकिन यह बराबरी () विधि दो वस्तुओं की समानता के बारे में है। जब हम वस्तु कहते हैं, तो उसके गुण होंगे। समानता के बारे में निर्णय लेने के लिए उन गुणों पर विचार किया जाता है। यह आवश्यक नहीं है कि समानता को निर्धारित करने के लिए सभी गुणों को ध्यान में रखा जाना चाहिए और वर्ग परिभाषा और संदर्भ के संबंध में यह तय किया जा सकता है। तब बराबरी () पद्धति को ओवरराइड किया जा सकता है।
जब भी हम समतुल्य () विधि को ओवरराइड करते हैं, तो हमें हमेशा हैशकोड () पद्धति को ओवरराइड करना चाहिए। यदि नहीं, तो क्या होगा? यदि हम अपने एप्लिकेशन में हैशटेबल्स का उपयोग करते हैं, तो यह अपेक्षा के अनुरूप व्यवहार नहीं करेगा। चूंकि हैशकोड का उपयोग संग्रहीत मूल्यों की समानता को निर्धारित करने में किया जाता है, यह एक कुंजी के लिए सही संगत मान नहीं लौटाएगा।
डिफ़ॉल्ट कार्यान्वयन दिया गया है हैशकोड () ऑब्जेक्ट क्लास में विधि ऑब्जेक्ट के आंतरिक पते का उपयोग करता है और इसे पूर्णांक में परिवर्तित करता है और इसे वापस करता है।
public class Tiger {
private String color;
private String stripePattern;
private int height;
@Override
public boolean equals(Object object) {
boolean result = false;
if (object == null || object.getClass() != getClass()) {
result = false;
} else {
Tiger tiger = (Tiger) object;
if (this.color == tiger.getColor()
&& this.stripePattern == tiger.getStripePattern()) {
result = true;
}
}
return result;
}
// just omitted null checks
@Override
public int hashCode() {
int hash = 3;
hash = 7 * hash + this.color.hashCode();
hash = 7 * hash + this.stripePattern.hashCode();
return hash;
}
public static void main(String args[]) {
Tiger bengalTiger1 = new Tiger("Yellow", "Dense", 3);
Tiger bengalTiger2 = new Tiger("Yellow", "Dense", 2);
Tiger siberianTiger = new Tiger("White", "Sparse", 4);
System.out.println("bengalTiger1 and bengalTiger2: "
+ bengalTiger1.equals(bengalTiger2));
System.out.println("bengalTiger1 and siberianTiger: "
+ bengalTiger1.equals(siberianTiger));
System.out.println("bengalTiger1 hashCode: " + bengalTiger1.hashCode());
System.out.println("bengalTiger2 hashCode: " + bengalTiger2.hashCode());
System.out.println("siberianTiger hashCode: "
+ siberianTiger.hashCode());
}
public String getColor() {
return color;
}
public String getStripePattern() {
return stripePattern;
}
public Tiger(String color, String stripePattern, int height) {
this.color = color;
this.stripePattern = stripePattern;
this.height = height;
}
}
उदाहरण कोड आउटपुट:
bengalTiger1 and bengalTiger2: true
bengalTiger1 and siberianTiger: false
bengalTiger1 hashCode: 1398212510
bengalTiger2 hashCode: 1398212510
siberianTiger hashCode: –1227465966
एक गेचा मुझे मिला है, जहां दो वस्तुओं में एक-दूसरे के संदर्भ होते हैं (एक उदाहरण माता-पिता / बच्चे का रिश्ता है जो सभी बच्चों को पाने के लिए माता-पिता की सुविधा पद्धति के साथ है)।
उदाहरण के लिए हाइबरनेट मैपिंग करते समय इस तरह की चीजें काफी आम हैं।
यदि आप अपने हैशकोड या समतुल्य परीक्षणों के दोनों सिरों को शामिल करते हैं तो एक पुनरावर्ती लूप में आना संभव है जो एक StackOverflowException में समाप्त होता है।
सरलतम उपाय तरीकों में getChildren संग्रह को शामिल नहीं करना है।
equals()
। अगर एक पागल वैज्ञानिक ने मुझे एक डुप्लिकेट बनाया तो हम समकक्ष होंगे। लेकिन हमारे पास एक ही पिता नहीं होगा।