Apache Commons बराबर / हैशकोड बिल्डर [बंद]


155

मुझे पता है कि उत्सुक हूँ, क्या लोगों को यहां का उपयोग कर के बारे में सोचने org.apache.commons.lang.builder EqualsBuilder/ HashCodeBuilder लागू करने के लिए equals/ hashCode? क्या अपना लिखने से बेहतर अभ्यास होगा? क्या यह हाइबरनेट के साथ अच्छा खेलता है? आपकी क्या राय है?


16
बस कार्यों reflectionEqualsऔर reflectionHashcodeकार्यों से मोह न हो ; प्रदर्शन एक निरपेक्ष हत्यारा है।
स्केफमैन

14
मैंने कल के बारे में यहाँ कुछ चर्चा देखी और कुछ खाली समय था, इसलिए मैंने एक त्वरित परीक्षण किया। मेरे पास अलग-अलग समान कार्यान्वयन वाले 4 ऑब्जेक्ट थे। ग्रहण उत्पन्न हुआ, समतुल्य है। आधार रेखा ग्रहण की थी। equalsbuilder.append ने 3.7x लिया। pojomatic ने 5x लिया। प्रतिबिंब आधारित 25.8x लिया। यह काफी हतोत्साहित करने वाला था क्योंकि मुझे परावर्तन आधारित सादगी पसंद है और मैं "पॉजोमैटिक" नाम नहीं रख सकता।
digitaljoel

5
एक अन्य विकल्प प्रोजेक्ट लोम्बोक है; यह प्रतिबिंब के बजाय बाइटकोड पीढ़ी का उपयोग करता है, इसलिए इसे ग्रहण-उत्पन्न के साथ-साथ प्रदर्शन करना चाहिए। projectlombok.org/features/EqualsAndHashCode.html
माइल्स

जवाबों:


212

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

यहाँ एक नमूना है बीन:

public class Bean{

    private String name;
    private int length;
    private List<Bean> children;

}

यहां कॉमन्स / लैंग के साथ बराबर () और हैशकोड () लागू किया गया है:

@Override
public int hashCode(){
    return new HashCodeBuilder()
        .append(name)
        .append(length)
        .append(children)
        .toHashCode();
}

@Override
public boolean equals(final Object obj){
    if(obj instanceof Bean){
        final Bean other = (Bean) obj;
        return new EqualsBuilder()
            .append(name, other.name)
            .append(length, other.length)
            .append(children, other.children)
            .isEquals();
    } else{
        return false;
    }
}

और यहाँ जावा 7 या उच्चतर (अमरूद से प्रेरित) के साथ:

@Override
public int hashCode(){
    return Objects.hash(name, length, children);
}

@Override
public boolean equals(final Object obj){
    if(obj instanceof Bean){
        final Bean other = (Bean) obj;
        return Objects.equals(name, other.name)
            && length == other.length // special handling for primitives
            && Objects.equals(children, other.children);
    } else{
        return false;
    }
}

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

जैसा कि आप देख सकते हैं कि अमरूद / जेडडीके संस्करण छोटा है और शानदार सहायक वस्तुओं से बचा जाता है। बराबरी के मामले में, यह मूल्यांकन को शॉर्ट-सर्कुलेट करने की भी अनुमति देता है यदि पहले वाला Object.equals()कॉल गलत होता है (निष्पक्ष होना: कॉमन्स / लैंग में ObjectUtils.equals(obj1, obj2)समान शब्दार्थ के साथ एक विधि है जिसका उपयोग EqualsBuilderउपरोक्त के बजाय शॉर्ट-सर्कुलेटिंग की अनुमति देने के लिए किया जा सकता है )।

तो: हाँ, मैन्युअल रूप से निर्मित equals()और hashCode()तरीकों (या उन भयानक राक्षस जो आपके लिए उत्पन्न होंगे) पर कॉमन्स लैंग बिल्डर्स बहुत बेहतर हैं , लेकिन जावा 7+ / अमरुद संस्करण और भी बेहतर हैं।

और हाइबरनेट के बारे में एक नोट:

अपने बराबर (), हैशकोड () और स्ट्रींग () कार्यान्वयन में आलसी संग्रह का उपयोग करने के बारे में सावधान रहें। यदि आपके पास एक खुला सत्र नहीं है तो यह बुरी तरह से विफल हो जाएगा।


नोट (बराबरी के बारे में):

a) उपरोक्त दोनों संस्करणों में (), आप इनमें से एक या दोनों शॉर्टकट का भी उपयोग करना चाह सकते हैं:

@Override
public boolean equals(final Object obj){
    if(obj == this) return true;  // test for reference equality
    if(obj == null) return false; // test for null
    // continue as above

b) बराबरी () अनुबंध की आपकी व्याख्या के आधार पर, आप भी लाइन बदल सकते हैं

    if(obj instanceof Bean){

सेवा

    // make sure you run a null check before this
    if(obj.getClass() == getClass()){ 

यदि आप दूसरे संस्करण का उपयोग करते हैं, तो आप शायद super(equals())अपने equals()तरीके के अंदर कॉल करना चाहते हैं । इस विषय पर राय अलग है, इस विषय पर चर्चा की गई है:

सुपरक्लास को अमरूद ऑब्जेक्टशकोड () के कार्यान्वयन में शामिल करने का सही तरीका?

(हालांकि इसके बारे में है hashCode(), वही लागू होता है equals())


नोट ( कयाहर से टिप्पणी से प्रेरित )

Objects.hashCode(..)(जैसा कि अंतर्निहित है Arrays.hashCode(...)) यदि आप कई आदिम क्षेत्र हैं तो बुरी तरह से प्रदर्शन कर सकते हैं। ऐसे मामलों में, EqualsBuilderवास्तव में बेहतर समाधान हो सकता है।


34
जावा 7 ऑब्जेक्ट्स के साथ भी ऐसा ही संभव होगा: download.oracle.com/javase/7/docs/api/java/util/…
थॉमस जंग

3
अगर मैं इसे सही ढंग से पढ़ रहा हूं, जोश बलोच ने प्रभावी जावा , आइटम 8 में कहा है, कि आपको अपने बराबर () विधि में getClass () का उपयोग नहीं करना चाहिए; बल्कि आपको इंस्टोफ़ का उपयोग करना चाहिए।
जेफ ओल्सन

6
@SeanPatrickFloyd अमरूद-मार्ग न केवल वर्गास के लिए एक सरणी वस्तु बनाता है, यह सभी मापदंडों को वस्तुओं में भी परिवर्तित करता है। तो जब आप इसे 10 int मान देते हैं तो आप 10 पूर्णांक ऑब्जेक्ट और एक सरणी ऑब्जेक्ट के साथ समाप्त हो जाएंगे। कॉमन्स-लैंग सॉल्यूशन केवल एक ही ऑब्जेक्ट बनाता है, भले ही आप हैश कोड में कितने मानों को जोड़ते हों। के साथ भी यही समस्या है equals। अमरूद सभी मूल्यों को वस्तुओं में परिवर्तित करता है, कॉमन्स-लैंग केवल एक नई वस्तु बनाता है।
कयाहर

1
@wonhee मैं दृढ़ता से असहमत हूं कि यह बेहतर है। हैश कोड की गणना करने के लिए प्रतिबिंब का उपयोग करना कुछ ऐसा नहीं है जो मैं कभी करूंगा। प्रदर्शन ओवरहेड शायद नगण्य है, लेकिन यह सिर्फ गलत लगता है।
सीन पैट्रिक फ्लोयड

1
@kaushik एक वर्ग का फ़ाइनल बनाते हुए वास्तव में दोनों संस्करणों की संभावित समस्याओं (उदाहरण और getClass ()) को हल करता है, जब तक आप केवल लीफ क्लासेस में अपने बराबर () को लागू करते हैं
सीन पैट्रिक फ्लोयड

18

दोस्तों, जागो! जावा 7 के बाद से मानक पुस्तकालय में बराबरी और हैशकोड के लिए सहायक तरीके हैं । उनका उपयोग अमरूद विधियों के उपयोग के पूरी तरह से समकक्ष है।


a) जिस समय यह प्रश्न पूछा गया था, Java 7 अभी तक b नहीं था) तकनीकी रूप से, वे काफी समकक्ष नहीं हैं। jdk में Objects.equals मेथड Guava की Objects.equal मेथड है। मैं केवल अमरूद के संस्करण के साथ स्थिर आयात का उपयोग कर सकता हूं। यह सिर्फ सौंदर्य प्रसाधन है, मुझे पता है, लेकिन यह गैर-अमरूद को और अधिक अव्यवस्थित बनाता है।
सीन पैट्रिक फ्लोयड

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

क्या आप एक उदाहरण दे सकते हैं, जब यह लूप में आता है?
मिखाइल गोलूबत्सोव

ओपी किसी वस्तु के भीतर समतुल्य () पद्धति को ओवरराइड करने के लिए कह रहा है। स्थैतिक विधि के दस्तावेज़ीकरण के अनुसार Objects.equals (): "सत्य सत्य है यदि तर्क प्रत्येक के लिए समान और असत्य हैं अन्यथा, यदि दोनों तर्क शून्य हैं, तो सत्य वापस आ जाता है और यदि कोई तर्क अशक्त, असत्य है। वापस लौटा। अन्यथा, पहले तर्क के बराबरी के तरीके का उपयोग करके समानता निर्धारित की जाती है। "इसलिए, अगर आपने ओवरसाइज़ किए गए उदाहरण के बराबर () में ऑब्जेक्शेसल्स () का उपयोग किया है, तो यह इसे खुद की बराबरी का तरीका कहेगा, फिर ऑब्जेक्शल्स () फिर खुद को फिर से, एक ढेर ओवरफ्लो दे रहा है।
दारु

@ डार्डो हम संरचनात्मक समानता को लागू करने के बारे में बात कर रहे हैं, तो इसका मतलब है कि दो वस्तुएं एक दूसरे के बराबर हैं यदि उनके क्षेत्र हैं। ऊपर अमरूद उदाहरण देखें, कैसे बराबर लागू किया जाता है।
मिखाइल गोलूबत्सोव

8

यदि आप 3rd पार्टी लाइब्रेरी पर निर्भर नहीं होना चाहते हैं (हो सकता है कि आप सीमित संसाधनों के साथ कोई डिवाइस चला रहे हों) और आप अपने खुद के तरीके भी टाइप नहीं करना चाहते हैं, तो आप आईडीई को काम करने दे सकते हैं, जैसे कि ग्रहण का उपयोग

Source -> Generate hashCode() and equals()...

आपको 'मूल' कोड मिलेगा जिसे आप अपनी इच्छानुसार कॉन्फ़िगर कर सकते हैं और जिसे आपको परिवर्तनों का समर्थन करना है।


उदाहरण (ग्रहण जूनो):

import java.util.Arrays;
import java.util.List;

public class FooBar {

    public String string;
    public List<String> stringList;
    public String[] stringArray;

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((string == null) ? 0 : string.hashCode());
        result = prime * result + Arrays.hashCode(stringArray);
        result = prime * result
                + ((stringList == null) ? 0 : stringList.hashCode());
        return result;
    }
    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        FooBar other = (FooBar) obj;
        if (string == null) {
            if (other.string != null)
                return false;
        } else if (!string.equals(other.string))
            return false;
        if (!Arrays.equals(stringArray, other.stringArray))
            return false;
        if (stringList == null) {
            if (other.stringList != null)
                return false;
        } else if (!stringList.equals(other.stringList))
            return false;
        return true;
    }

}

14
यह सच है, लेकिन ग्रहण द्वारा उत्पन्न कोड अप्राप्य और अप्राप्य है।
सीन पैट्रिक फ्लोयड

6
कृपया, कभी भी ग्रहण-उत्पन्न के रूप में भयानक चीज़ के बारे में कभी न सोचें equals। यदि आप 3rd पार्टी लाइब्रेरी पर निर्भर नहीं होना चाहते हैं, तो Objects.equalअपने जैसी एक-पंक्ति विधि लिखें । यहां तक ​​कि जब केवल एक या दो बार उपयोग किया जाता है, तो यह कोड को बेहतर बनाता है!
माॅर्टिनस

@aaartinus equals/ hashCodeएक पंक्ति विधियाँ ???
FrVaBe 17

1
@maaartinus Guava एक 3rd पार्टी लाइब्रेरी है। मैंने बताया कि मेरे समाधान का उपयोग किया जा सकता है यदि आप तीसरे पक्ष के पुस्तकालयों का उपयोग करके AVOID करना चाहते हैं।
FrVaBe

1
@FrVaBe: और मैंने लिखा है "यदि आप तृतीय पक्ष पुस्तकालय पर निर्भर नहीं होना चाहते हैं, तो ऑब्जेक्ट-असेंबली की तरह एक-पंक्ति विधि स्वयं लिखें।" और फिर मैंने एक-लाइन विधि लिखी जिसका उपयोग आप अमरूद का उपयोग करके कर सकते हैं और अभी भी लगभग एक आधे के बराबर लंबाई काटते हैं।
Maaartinus

6

द इक्वाल्सबर्स्ट और हैशकोडब्यूस्टल के दो मुख्य पहलू हैं जो मैन्युअल रूप से लिखे गए कोड से अलग हैं:

  • अशक्त संभालना
  • उदाहरण सृजन

इक्वाल्सबर्स्ट और हैशकोडब्यूस्टल उन क्षेत्रों की तुलना करना आसान बनाते हैं जो अशक्त हो सकते हैं। मैन्युअल रूप से रिटेन कोड के साथ यह बहुत सारे बॉयलरप्लेट बनाता है।

समतुल्य राशि दूसरी ओर बराबरी विधि कॉल के प्रति एक उदाहरण पैदा करेगा। यदि आपकी बराबरी के तरीके अक्सर कॉल होते हैं तो यह बहुत सारे उदाहरण देगा।

हाइबरनेट के लिए बराबर और हैशकोड कार्यान्वयन से कोई फर्क नहीं पड़ता। वे सिर्फ एक कार्यान्वयन विवरण हैं। बिल्डर के रनटाइम ओवरहेड (यहां तक ​​कि बिना भागने के विश्लेषण के साथ) लोड किए गए लगभग सभी डोमेन ऑब्जेक्ट्स को अनदेखा किया जा सकता है । डेटाबेस और संचार ओवरहेड महत्वपूर्ण होगा।

जैसा कि स्कैफ़मैन ने उल्लेख किया है कि प्रतिबिंब संस्करण का उपयोग उत्पादन कोड में नहीं किया जा सकता है। प्रतिबिंब धीमा करना होगा और "कार्यान्वयन" सभी के लिए सही नहीं होगा लेकिन सबसे सरल वर्ग। सभी सदस्यों को ध्यान में रखना भी खतरनाक है क्योंकि नए शुरू किए गए सदस्य समान पद्धति के व्यवहार को बदलते हैं। प्रतिबिंब संस्करण परीक्षण कोड में उपयोगी हो सकता है।


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

1
@digitaljoel हाँ, आप फ़ील्ड्स को बाहर कर सकते हैं, लेकिन ये परिभाषाएँ बचत को वापस नहीं ला रही हैं। इसलिए मैंने उन्हें उद्देश्य के बारे में नहीं बताया।
थॉमस जंग


0

यदि आप केवल इकाई बीन के साथ काम कर रहे हैं जहां आईडी एक प्राथमिक कुंजी है, तो आप सरल कर सकते हैं।

   @Override
   public boolean equals(Object other)
   {
      if (this == other) { return true; }
      if ((other == null) || (other.getClass() != this.getClass())) { return false; }

      EntityBean castOther = (EntityBean) other;
      return new EqualsBuilder().append(this.getId(), castOther.getId()).isEquals();
   }

0

मेरी राय में यह हाइबरनेट के साथ अच्छा नहीं खेलता है, विशेष रूप से कुछ इकाई के लिए लंबाई, नाम और बच्चों की तुलना करने वाले उत्तर से उदाहरण। हाइबरनेट व्यापार कुंजी का उपयोग बराबरी () और हैशकोड () में करने की सलाह देता है , और उनके पास इसके कारण हैं। यदि आप अपने व्यापार कुंजी पर ऑटो बराबर () और हैशकोड () जनरेटर का उपयोग करते हैं, तो यह ठीक है, बस प्रदर्शन समस्याओं को पहले से उल्लेख किए जाने की आवश्यकता है। लेकिन लोग आमतौर पर सभी गुणों का उपयोग करते हैं जो आईएमओ बहुत गलत है। उदाहरण के लिए, मैं वर्तमान में प्रोजेक्ट पर काम कर रहा हूं, जहां पोएजोमैटिक के साथ @AutoProperty का उपयोग करके इकाइयां लिखी गई हैं, जिसे मैं वास्तव में खराब पैटर्न मानता हूं।

हैशकोड () और बराबर () का उपयोग करने के लिए उनके दो मुख्य परिदृश्य हैं:

  • जब आप एक सेट में लगातार कक्षाओं के उदाहरण डालते हैं (कई मूल्यवान संघों का प्रतिनिधित्व करने के लिए अनुशंसित तरीका) और
  • जब आप अलग किए गए उदाहरणों के पुनर्लेखन का उपयोग करते हैं

तो चलिए मान लेते हैं कि हमारी इकाई इस तरह दिखती है:

class Entity {
  protected Long id;
  protected String someProp;
  public Entity(Long id, String someProp);
}

Entity entity1 = new Entity(1, "a");
Entity entity2 = new Entity(1, "b");

दोनों हाइबरनेट के लिए एक ही इकाई हैं, जो कुछ बिंदु पर कुछ सत्र से प्राप्त किए गए हैं (उनकी आईडी और कक्षा / तालिका समान हैं)। लेकिन जब हम ऑटो प्रोपल्स () हैशकोड () सभी प्रॉप्स पर लागू करते हैं, तो हमारे पास क्या है?

  1. जब आप निकाय 2 को लगातार सेट पर रखते हैं, जहां पहले से ही मौजूद है, तो यह दो बार डाला जाएगा और इसके परिणामस्वरूप कमिट किया जाएगा।
  2. यदि आप अलग किए गए निकाय 2 को सत्र में संलग्न करना चाहते हैं, जहां पहले से ही वे मौजूद हैं, तो (शायद, मैंने इसे विशेष रूप से परीक्षण नहीं किया है) ठीक से विलय नहीं किया जाएगा।

इसलिए, 99% परियोजना के लिए, हम आधार इकाई वर्ग में एक बार लिखे गए समान () और हैशकोड () के निम्नलिखित कार्यान्वयन का उपयोग करते हैं, जो हाइबरनेट अवधारणाओं के अनुरूप है:

@Override
public boolean equals(Object obj) {
    if (StringUtils.isEmpty(id))
        return super.equals(obj);

    return getClass().isInstance(obj) && id.equals(((IDomain) obj).getId());
}

@Override
public int hashCode() {
    return StringUtils.isEmpty(id)
        ? super.hashCode()
        : String.format("%s/%s", getClass().getSimpleName(), getId()).hashCode();
}

क्षणिक इकाई के लिए मैं वही करता हूं जो हाइबरनेट दृढ़ता कदम पर करेगा, अर्थात। मैं इंस्टेंस मैच का उपयोग करता हूं। लगातार वस्तुओं के लिए मैं अद्वितीय कुंजी की तुलना करता हूं, जो कि तालिका / आईडी है (मैं कभी भी समग्र कुंजियों का उपयोग नहीं करता हूं)।


0

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

उपयोग उदाहरण:

public int hashCode() {
    return HashCode.hash(HashCode.hash(timestampMillis), name, dateOfBirth); // timestampMillis is long
}

public int hashCode() {
    return HashCode.hash(super.hashCode(), occupation, children);
}

हैशकोड सहायक:

public class HashCode {

    public static int hash(Object o1, Object o2) {
        return add(Objects.hashCode(o1), o2);
    }

    public static int hash(Object o1, Object o2, Object o3) {
        return hash(Objects.hashCode(o1), o2, o3);
    }

    ...

    public static int hash(Object o1, Object o2, ..., Object o10) {
        return hash(Objects.hashCode(o1), o2, o3, ..., o10);
    }

    public static int hash(int initial, Object o1, Object o2) {
        return add(add(initial, o1), o2);
    }

    ...

    public static int hash(int initial, Object o1, Object o2, ... Object o10) {
        return add(... add(add(add(initial, o1), o2), o3) ..., o10);
    }

    public static int hash(long value) {
        return (int) (value ^ (value >>> 32));
    }

    public static int hash(int initial, long value) {
        return add(initial, hash(value));
    }

    private static int add(int accumulator, Object o) {
        return 31 * accumulator + Objects.hashCode(o);
    }
}

मुझे लगा है कि 10 एक डोमेन मॉडल में गुणों की अधिकतम उचित संख्या है, यदि आपके पास स्ट्रिंग्स और प्राइमेटिक्स के ढेर को बनाए रखने के बजाय अधिक क्लास को फिर से शुरू करने और शुरू करने के बारे में सोचना चाहिए।

कमियां हैं: यह उपयोगी नहीं है यदि आपके पास मुख्य रूप से आदिम और / या सरणियाँ हैं जिन्हें आपको गहराई से करने की आवश्यकता है। (सामान्य रूप से यह मामला है जब आपको फ्लैट (स्थानांतरण) वस्तुओं से निपटना पड़ता है जो आपके नियंत्रण से बाहर है)।

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