2012-06-06 6 views
22

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

public class Admin { 

    private Long id; 
    private String userName; 

    public String getUserName() { 
     return userName; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (!(o instanceof Admin)) { 
      return false; 
     } 
     Admin otherAdmin = (Admin) o; 
     EqualsBuilder builder = new EqualsBuilder(); 
     builder.append(getUserName(), otherAdmin.getUserName()); 
     return builder.isEquals(); 
    } 

    @Override 
    public int hashCode() { 
     HashCodeBuilder builder = new HashCodeBuilder(); 
     builder.append(getUserName()); 
     return builder.hashCode(); 
    } 
} 

बनाम।

public class Admin { 

    private Long id; 
    private String userName; 

    public String getUserName() { 
     return userName; 
    } 

    @Override 
    public boolean equals(Object o) { 
     return EqualsBuilder.reflectionEquals(this, o, Arrays.asList(id)); 
    } 

    @Override 
    public int hashCode() { 
     return HashCodeBuilder.reflectionHashCode(this, Arrays.asList(id)); 
    } 
} 
+0

आपको नीचे दिए गए एक प्रश्न को स्वीकार करने की आवश्यकता है जो आपके प्रश्न के सूट है। मेरे पास भी एक ही सवाल है और यह उचित नहीं ठहरा सकता है कि नीचे दिए गए उत्तरों से अनुष्ठान कौन सा है। किसी भी उत्तर को स्वीकार करने का प्रयास करें। – developer

+2

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

उत्तर

9

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

यदि मैं आपकी स्थिति में था तो मैं पहले विकल्प के लिए जाऊंगा। हैशकोड उत्पन्न करने में आपके पहले दृष्टिकोण में भी एक गलती है, इसे वापस builder.toHashCode() होना चाहिए; वापसी builder.hashCode() के बजाय , (जो hashCode बिल्डर रिटर्न वस्तुओं hashCode)

+1

मैं हैशकोड() के बजाय हैशकोड() के बजाय हैशकोड() का उपयोग कर सकता हूं, हैशकोडबिल्डर, हैशकोड() विधि के अद्यतन कार्यान्वयन के अनुसार अब हैशकोड() http://commons.apache.org/lang/api-2.6/index.html पर कॉल करता है? संगठन/apache/commons/lang/builder/HashCodeBuilder.html – tintin

+2

मजेदार तथ्य: EqualsBuilder.equals() EqualsBuilder.isEquals() को कॉल नहीं करता है। क्या यह एक बुरा जाल नहीं है ?! : -/ –

9

2 कारणों के लिए दूसरा विकल्प पसंद करेंगे:

  1. जाहिर है यह आसान है पढ़ने के लिए

  2. मैं पहले विकल्प के लिए प्रदर्शन तर्क नहीं खरीदूंगा, जब तक कि उनमें कोई प्रासंगिक मीट्रिक शामिल न हो। ईजी। कितने मिलीसेकंड प्रतिबिंब-आधारित "बराबर" एक सामान्य अंत-टू-एंड अनुरोध विलंबता में जोड़ देंगे? कुल मिलाकर, क्या% वृद्धि होगी? जानते हुए भी कि अच्छा संभावना के बिना हैं कि अनुकूलन समय से पहले

3

आपका प्रश्न के रूप में स्पष्ट रूप से लिखा दूसरा दृष्टिकोण के लाभों में से एक को दिखाता है:

पहले मामले में, यह है बहुत बनाने के लिए आसान गलती return builder.hashCode(), सही return builder.toHashCode() के बजाय, जिसके परिणामस्वरूप सूक्ष्म त्रुटियां हो सकती हैं जो ट्रैक करने में बहुत मुश्किल हो सकती हैं।

दूसरा मामला इस टाइपो की संभावना को समाप्त करता है, जिसके परिणामस्वरूप बग को खोजने की कोशिश करने वाले कीबोर्ड में आपके सिर को कम टक्कर मिलती है।

+0

यह टिप्पणी अपाचे कॉमन्स v2.5 या नए के लिए गलत है, अपाचे कॉमन्स v2.5 फरवरी 2010 में रिलीज़ हुई थी। जावाडोक को उद्धृत करने के लिए: "गणना की हैशकोड से हैशकोड() को बग की संभावना के कारण वापस कर दिया गया है हैशकोड() को गलत कॉलिंग और इसकी संभावना नहीं है कि हैशकोडबिल्डर के लिए हैशकोड क्या है। " जावाडोक यहां उपलब्ध है: http://commons.apache.org/proper/commons-lang/release-history.html – cfogelberg

+0

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

2

मैं कहूंगा कि इनमें से कोई भी अच्छा कार्यान्वयन नहीं है। मैं तर्क दूंगा कि EqualsBuilder निम्नलिखित कारणों से उपयोग करने के लिए एक अच्छा ढांचा नहीं है:

  1. विस्तार योग्य नहीं है। क्या होगा यदि आप जिस क्षेत्र में समानता का दावा करने की कोशिश कर रहे हैं, उसमें से एक को बराबर और खाली के बराबर माना जाना चाहिए?
  2. आपको चर की सूची को बनाए रखना चाहिए जैसे कि वे हार्डकोडेड चर हैं। मतलब है कि आपको उन सभी चरों को सूचीबद्ध करना होगा जिन्हें आप तुलना करना चाहते हैं। इस बिंदु पर == o.getA() & & बी == o.getB() ...
  3. प्रतिबिंब का उपयोग करके अतिरिक्त संसाधन लेता है जैसा कि आपने बताया है, और एक उद्यम में आवेदन जो अरबों वस्तुओं को कुचल देता है। इस प्रतिबिंब के बराबर करना एक स्मृति रिसाव के रूप में बुरा है।

मैं कहूंगा कि अपाचे से बेहतर फ्रेमवर्क होना आवश्यक है।

+0

1। सुनिश्चित करें कि गुण या तो नल या खाली तार हैं (उदाहरण के लिए सेटर्स में)। समस्या सुलझ गयी। –

10

हालांकि दूसरा विकल्प अधिक आकर्षक है (क्योंकि यह कोड की केवल एक पंक्ति है) मैं पहला विकल्प चुनूंगा।

कारण बस प्रदर्शन है। एक छोटे से परीक्षण चलाने के बाद मुझे उनके बीच एक बहुत अच्छा समय अंतर मिला।

package equalsbuildertest; 

import java.math.BigDecimal; 
import java.util.Date; 

import org.apache.commons.lang3.builder.EqualsBuilder; 
import org.apache.commons.lang3.builder.HashCodeBuilder; 

public class Class1 { 

    private int field1; 

    private boolean field2; 

    private BigDecimal field3; 

    private String field4; 

    private Date field5; 

    private long field6; 

    public Class1(int field1, boolean field2, BigDecimal field3, String field4, 
      Date field5, long field6) { 
     super(); 
     this.field1 = field1; 
     this.field2 = field2; 
     this.field3 = field3; 
     this.field4 = field4; 
     this.field5 = field5; 
     this.field6 = field6; 
    } 

    public Class1() { 
     super(); 
    } 

    public int getField1() { 
     return field1; 
    } 

    public void setField1(int field1) { 
     this.field1 = field1; 
    } 

    public boolean isField2() { 
     return field2; 
    } 

    public void setField2(boolean field2) { 
     this.field2 = field2; 
    } 

    public BigDecimal getField3() { 
     return field3; 
    } 

    public void setField3(BigDecimal field3) { 
     this.field3 = field3; 
    } 

    public String getField4() { 
     return field4; 
    } 

    public void setField4(String field4) { 
     this.field4 = field4; 
    } 

    public Date getField5() { 
     return field5; 
    } 

    public void setField5(Date field5) { 
     this.field5 = field5; 
    } 

    public long getField6() { 
     return field6; 
    } 

    public void setField6(long field6) { 
     this.field6 = field6; 
    } 

    @Override 
    public boolean equals(Object o) { 
     return EqualsBuilder.reflectionEquals(this, o); 
    } 

    @Override 
    public int hashCode() { 
     return HashCodeBuilder.reflectionHashCode(this); 
    } 

} 

और:: द्वितीय श्रेणी काफी पहले एक रूप में ही है

package equalsbuildertest; 

import java.math.BigDecimal; 
import java.util.Date; 

import org.apache.commons.lang3.builder.EqualsBuilder; 
import org.apache.commons.lang3.builder.HashCodeBuilder; 

public class Class2 { 

    private int field1; 

    private boolean field2; 

    private BigDecimal field3; 

    private String field4; 

    private Date field5; 

    private long field6; 

    public Class2(int field1, boolean field2, BigDecimal field3, String field4, 
      Date field5, long field6) { 
     super(); 
     this.field1 = field1; 
     this.field2 = field2; 
     this.field3 = field3; 
     this.field4 = field4; 
     this.field5 = field5; 
     this.field6 = field6; 
    } 

    public Class2() { 
     super(); 
    } 

    public int getField1() { 
     return field1; 
    } 

    public void setField1(int field1) { 
     this.field1 = field1; 
    } 

    public boolean isField2() { 
     return field2; 
    } 

    public void setField2(boolean field2) { 
     this.field2 = field2; 
    } 

    public BigDecimal getField3() { 
     return field3; 
    } 

    public void setField3(BigDecimal field3) { 
     this.field3 = field3; 
    } 

    public String getField4() { 
     return field4; 
    } 

    public void setField4(String field4) { 
     this.field4 = field4; 
    } 

    public Date getField5() { 
     return field5; 
    } 

    public void setField5(Date field5) { 
     this.field5 = field5; 
    } 

    public long getField6() { 
     return field6; 
    } 

    public void setField6(long field6) { 
     this.field6 = field6; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (!(obj instanceof Class2)) { 
      return false; 
     } 
     Class2 other = (Class2) obj; 
     EqualsBuilder builder = new EqualsBuilder(); 
     builder.append(field1, other.field1); 
     builder.append(field2, other.field2); 
     builder.append(field3, other.field3); 
     builder.append(field4, other.field4); 
     builder.append(field5, other.field5); 
     builder.append(field6, other.field6); 
     return builder.isEquals(); 
    } 

    @Override 
    public int hashCode() { 
     HashCodeBuilder builder = new HashCodeBuilder(); 
     builder.append(getField1()); 
     builder.append(isField2()); 
     builder.append(getField3()); 
     builder.append(getField4()); 
     builder.append(getField5()); 
     builder.append(getField6()); 
     return builder.hashCode(); 

    }; 

} 

कि

आदेश एक तरह से समय की एक विचार पाने के लिए, मैं इस दो सरल कक्षाओं बनाया , लेकिन विभिन्न बराबर और हैशकोड के साथ।

package equalsbuildertest; 

import static org.junit.Assert.*; 

import java.math.BigDecimal; 
import java.util.Date; 

import org.junit.Test; 

public class EqualsBuilderTest { 

    @Test 
    public void test1() { 
     Class1 class1a = new Class1(1, true, new BigDecimal(0), "String", new Date(), 1L); 
     Class1 class1b = new Class1(1, true, new BigDecimal(0), "String", new Date(), 1L); 
     for (int i = 0; i < 1000000; i++) { 
      assertEquals(class1a, class1b); 
     } 
    } 

    @Test 
    public void test2() { 
     Class2 class2a = new Class2(1, true, new BigDecimal(0), "String", new Date(), 1L); 
     Class2 class2b = new Class2(1, true, new BigDecimal(0), "String", new Date(), 1L); 
     for (int i = 0; i < 1000000; i++) { 
      assertEquals(class2a, class2b); 
     } 
    } 

} 

परीक्षण बहुत सरल कर रहे हैं और केवल समय को मापने के लिए कार्य करता है:

उसके बाद, मैं निम्नलिखित परीक्षण बनाया।

परिणाम थे निम्नलिखित:

  • test1 (2,024 रों)
  • test2 (0039 रों)

मैं उन्हें चुना है आदेश के लिए पूरी तरह से बराबर हो सबसे बड़ा समय। यदि आप NotEquals स्थितियों के साथ परीक्षण करना चुनते हैं तो आपके पास कम समय होगा, लेकिन बहुत बड़ा समय अंतर भी बनाएगा।

मैं फेडोरा 21 और ग्रहण लूना के साथ एक 64-बिट इंटेल कोर i5-3317U सीपीयू @ 1.70GHz x4 पर इस परीक्षण चला।

निष्कर्ष में, मैं कोड की कुछ पंक्तियों को सहेजने के लिए इस तरह के एक महान प्रदर्शन अंतर को जोखिम नहीं उठाऊंगा जिसे आप शायद टेम्पलेट का उपयोग करके टाइप नहीं कर सकते हैं (विंडोज़ के तहत ग्रहण में -> प्राथमिकता जावा में मिलती है -> संपादक -> टेम्पलेट) इस जैसे:

${:import(org.apache.commons.lang3.builder.HashCodeBuilder, org.apache.commons.lang3.builder.EqualsBuilder)} 
@Override 
public int hashCode() { 
    HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(); 
    hashCodeBuilder.append(${field1:field}); 
    hashCodeBuilder.append(${field2:field}); 
    hashCodeBuilder.append(${field3:field}); 
    hashCodeBuilder.append(${field4:field}); 
    hashCodeBuilder.append(${field5:field}); 
    return hashCodeBuilder.toHashCode(); 
} 

@Override 
public boolean equals(Object obj) { 
    if (this == obj) { 
     return true; 
    } 
    if (obj == null) { 
     return false; 
    } 
    if (getClass() != obj.getClass()) { 
     return false; 
    } 
    ${enclosing_type} rhs = (${enclosing_type}) obj; 
    EqualsBuilder equalsBuilder = new EqualsBuilder(); 
    equalsBuilder.append(${field1}, rhs.${field1}); 
    equalsBuilder.append(${field2}, rhs.${field2}); 
    equalsBuilder.append(${field3}, rhs.${field3}); 
    equalsBuilder.append(${field4}, rhs.${field4}); 
    equalsBuilder.append(${field5}, rhs.${field5});${cursor} 
    return equalsBuilder.isEquals(); 
} 
1

@Churk, अपाचे HashCodeBuilder और EqualsBuilder साथ सहमत अच्छी तरह से लागू नहीं कर रहे हैं। हैशकोडबिल्डर अभी भी प्राइम नंबरों के साथ खेल रहा है! इसके अलावा, यह एक बहुत ही अनावश्यक काम करता है। क्या आपने स्रोत पढ़ा है?

जावा 5 के बाद से (यदि पहले नहीं है), सारस्ट्रैश मैप <> हैश बाल्टी का पता लगाने के लिए एक प्रमुख संख्या के मॉड्यूल का उपयोग नहीं किया है। इसके बजाए, बाल्टी की संख्या दो की शक्ति है और बाल्टी का पता लगाने के लिए हैश कोड के कम क्रम एन बिट्स का उपयोग किया जाता है।

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

इस प्रकार, पूर्णांक Object.hashCode ओवरराइड करने के लिए() सही तरीका वस्तुओं कि किसी भी संग्रह में cohabitate वर्ग का उपयोग कर होगा की आबादी में सबसे ज्यादा arity के साथ सरल, निरंतर मूल्य वापस लौट कर रहा है।

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

बेशक, हैशकोड() और बराबर() के बीच अनुबंध पूरा होना चाहिए। इस प्रकार, बराबर() तदनुसार लागू किया जाना चाहिए। हैशकोड() को समानता के लिए आवश्यक पूर्ण क्वालीफायरों का उपयोग करने की आवश्यकता नहीं है, लेकिन हैशकोड() में उपयोग किए जाने वाले किसी भी फ़ील्ड को बराबर() में उपयोग किया जाना चाहिए। यहां, StringUtils.equals (s1, s2) जैसी विधियां निरंतर और सुरक्षित रूप से शून्य मानों को संभालने के लिए उपयोगी हैं।

+0

और भी महत्वपूर्ण: HashCodeBuilder.reflectionHashCode() और EqualsBuilder.reflectionEquals() वास्तव में कुछ हद तक खतरनाक हैं कि वे आसानी से बराबर()/हैशकोड() अनुबंध के "संगत" हिस्से का उल्लंघन कर सकते हैं। यहां क्या होता है: यदि किसी ऑब्जेक्ट को हैश टेबल/किसी प्रकार के सेट में रखा गया है और बाद में, हैशकोड() या बराबर() में उपयोग किया जाने वाला फ़ील्ड बदल जाता है, तो ऑब्जेक्ट को संग्रह में प्रभावी ढंग से अनाथ कर दिया जाता है और इसे कभी भी एक्सेस नहीं किया जा सकता । केवल invariant, पहचानकर्ता फ़ील्ड हैशकोड() या बराबर() में उपयोग किया जाना चाहिए। डेवलपर इस विकल्प को सामान्य "बिल्डर" में नहीं छोड़ सकता है। संग्रह में वस्तुओं को सम्मिलित करने के बाद – Charlie

+0

किसी भी बराबर/हैश कार्यान्वयन के लिए एक समस्या है – Bax

संबंधित मुद्दे