ما هي البدائل مقارنة المساواة بين اثنين من الكائنات ؟

StackOverflow https://stackoverflow.com/questions/360265

  •  21-08-2019
  •  | 
  •  

سؤال

http://leepoint.net/notes-java/data/expressions/22compareobjects.html

اتضح أن تحديد يساوي() ليست تافهة ، في الواقع انها معتدلة من الصعب الحصول على ذلك الحق ، وخاصة في حالة من الفئات الفرعية.أفضل معالجة القضايا في Horstmann الأساسية جافا المجلد 1.

إذا كان يساوي() يجب أن يكون دائما تجاوزها ، ثم ما هو نهج جيد لا يحشر في الحاجة إلى القيام وجوه المقارنة ؟ ما هي بعض جيدة "تصميم" البدائل ؟

تحرير:

أنا متأكد من أن هذا هو قادم عبر الطريق الذي كنت أقصده.ربما ينبغي أن يكون السؤال أكثر على غرار "لماذا تريد أن المقارنة بين اثنين من الكائنات؟" استنادا إلى الإجابة على هذا السؤال ، هل هناك حل بديل المقارنة ؟ أنا لا أقصد مختلفة تنفيذ يساوي.أعني عدم استخدام المساواة في كل شيء.أعتقد أن النقطة الأساسية هي أن تبدأ مع هذا السؤال, لماذا تريد أن المقارنة بين اثنين من الكائنات.

هل كانت مفيدة؟

المحلول

إذا كان يساوي() يجب أن يكون دائما تخطيه ، ثم ما هو نهج جيد لا كونه يحشر إلى الحاجة إلى القيام وجوه المقارنة ؟

أنت مخطئ.يجب عليك تجاوز يساوي كما نادرا ما ممكن.


كل هذه المعلومات تأتي من فعالة جافا ، الطبعة الثانية (جوش بلوك).الطبعة الأولى الفصل على هذا لا يزال متاح مجانا تحميل.

من فعالية جافا:

أسهل طريقة لتجنب المشاكل عدم تجاوز أسلوب يساوي ، هذه الحالة كل مثيل من فئة تساوي سوى نفسها.

المشكلة مع تعسفا تجاوز يساوي/hashCode هو الميراث.بعض يساوي تطبيقات الدعوة اختبار مثل هذا:

if (this.getClass() != other.getClass()) {
    return false; //inequal
}

في الواقع ، الكسوف (3.4) محرر جافا لا فقط هذا عند إنشاء طريقة استخدام أدوات المصدر.وفقا بلوخ ، وهذا خطأ لأنه يخالف Liskov مبدأ الإحلال.

من فعالية جافا:

لا توجد طريقة توجيه instantiable فئة وإضافة القيمة العنصر مع الحفاظ على تساوي العقد.

طريقتان لتقليل المساواة المشاكل الموضحة في فئات و واجهات الفصل:

  1. لصالح تكوين على الميراث
  2. تصميم وثيقة الميراث أو آخر تحظر ذلك

بقدر ما أستطيع أن أرى ، فإن البديل الوحيد هو اختبار المساواة في شكل الخارجية إلى فئة ، و كيف يمكن أن يكون إجراء من شأنه أن تعتمد على تصميم من نوع سياق كنت تحاول استخدامه في.

على سبيل المثال ، يمكنك تحديد واجهة الوثائق كيف كان يمكن مقارنتها.في البرمجية أدناه, خدمة الحالات قد يتم استبدال في وقت التشغيل مع إصدار أحدث من نفس الفئة - في حال وجود مختلف ClassLoaders يساوي المقارنات دائما return false, حتى تجاوز يساوي/hashCode ستكون زائدة عن الحاجة.

public class Services {

    private static Map<String, Service> SERVICES = new HashMap<String, Service>();

    static interface Service {
        /** Services with the same name are considered equivalent */
        public String getName();
    }

    public static synchronized void installService(Service service) {
        SERVICES.put(service.getName(), service);
    }

    public static synchronized Service lookup(String name) {
        return SERVICES.get(name);
    }
}

"لماذا تريد أن المقارنة بين اثنين من الكائنات؟"

مثال واضح هو لاختبار ما إذا كان اثنين من السلاسل هي نفسها (أو اثنين الملفات, أو محددات).على سبيل المثال ، ما إذا كنت تريد بناء مجموعة من الملفات إلى تحليل.بحكم التعريف ، يحتوي على مجموعة فقط من عناصر فريدة من نوعها.جافا مجموعة نوع يعتمد على يساوي/hashCode فرض تفرد عناصرها.

نصائح أخرى

وأنا لا أعتقد أنه صحيح أن متساوين يجب أن يتم تجاوز دائما. حكم كما أفهمها هي أن يساوي الأسمى هو معنى إلا في الحالات التي كنت واضحا حول كيفية تحديد الأشياء أي ما يعادل غويا. في هذه الحالة، يمكنك تجاوز شفرة التجزئة ()، وكذلك بحيث لم يكن لديك الكائنات التي قمت بتحديدها كما تعادل العودة hashcodes مختلفة.

إذا كنت لا تستطيع تحديد التكافؤ معنى، وأنا لا أرى فائدة.

وماذا عن تفعل ذلك الحق؟

إليك يا يساوي القالب الذي هو المعرفة التطبيقية من جاوة الفعالة من قبل جوش بلوك. قراءة الكتاب لمزيد من التفاصيل:

@Override
public boolean equals(Object obj) {
    if(this == obj) {
        return true;
    }

    // only do this if you are a subclass and care about equals of parent
    if(!super.equals(obj)) {
        return false;
    }
    if(obj == null || getClass() != obj.getClass()) {
        return false;
    }
    final YourTypeHere other = (YourTypeHere) obj;
    if(!instanceMember1.equals(other.instanceMember1)) {
       return false;
     }
     ... rest of instanceMembers in same pattern as above....
     return true;
 }

وMmhh

في بعض الحالات يمكنك جعل unmodifiable كائن (للقراءة فقط)، وأنها خلقت من نقطة واحدة (طريقة المصنع)

وإذا كانت هناك حاجة كائنين مع إدخال البيانات نفسها (معلمات الخلق) والمصنع بإرجاع نفس المثال المرجع ثم استخدام "==" سيكون كافيا.

وهذه الطريقة مفيدة في ظل ظروف معينة فقط. وأكثر من مرة أن ننظر مبالغة.

ونلقي نظرة على هذه الإجابة لمعرفة كيفية تنفيذ شيء من هذا القبيل.

محذرا من أنه هو الكثير من التعليمات البرمجية

لباختصار نرى كيف يعمل فئة المجمع <لأ href = "http://java.sun.com/javase/6/docs/api/java/lang/Integer.html#valueOf(int)" يختلط = " نوفولو noreferrer "> منذ جافا 1.5

Integer a = Integer.valueOf( 2 );
Integer b = Integer.valueOf( 2 );

a == b 

وهذا صحيح في حين

new Integer( 2 ) == new Integer( 2 )  

وغير صحيح.

وإنها تحافظ داخليا المرجعية وإعادته إذا كانت قيمة المدخلات هي نفسها.

وكما تعلمون صحيح للقراءة فقط

وشيء مشابه يحدث مع فئة سلسلة من الذي كان على هذا السؤال عنها.

ربما أنا في عداد المفقودين نقطة ولكن السبب الوحيد لاستخدام يساوي بدلا من تحديد الأسلوب الخاص بك مع اسم مختلف لأن العديد من مجموعات (وربما غيرها من الاشياء في JDK أو ما يطلق عليه هذه الأيام) نتوقع يساوي طريقة لتحديد نتائج مترابطة.ولكن أبعد من ذلك, أستطيع أن أفكر في ثلاثة أنواع من المقارنات التي تريد القيام به في يساوي:

  1. اثنين من الأشياء حقا هي نفس الحالة.هذا لا معنى لاستخدام يساوي لأنه يمكنك استخدام ==.أيضا ، و تصحيح لي إذا كنت قد نسيت كيف يعمل في جافا الافتراضية يساوي الطريقة لا تستخدم هذا تلقائيا تجزئة رموز.
  2. اثنين من الأشياء قد يشير إلى نفس الحالات ، ولكن ليس على نفس الدرجة.هذا هو مفيدة أحيانا...ولا سيما إذا كانت قائمة الكائنات الرجوع إلى نفس الكائن في ديسيبل.سيكون لديك لتحديد يساوي طريقة للقيام بذلك.
  3. اثنين من الكائنات تحتوي على مراجع إلى الكائنات التي هي متساوية في القيمة ، على الرغم من أنها قد تكون أو لا تكون نفس الحالات (وبعبارة أخرى ، مقارنة القيم على طول الطريق من خلال التسلسل الهرمي).

لماذا تريد أن المقارنة بين اثنين من الكائنات ؟ حسنا, إذا كانت متساوية ، كنت تريد أن تفعل شيئا واحدا, و إذا كنت لا تريد أن تفعل شيئا آخر.

وقال أن ذلك يعتمد على الحالة في متناول اليد.

والسبب الرئيسي لتجاوز يساوي () في معظم الحالات هو للتحقق من التكرار في بعض المجموعات. على سبيل المثال، إذا كنت ترغب في استخدام مجموعة لاحتواء كائن قمت بإنشائها تحتاج إلى تجاوز يساوي () وشفرة التجزئة () داخل الكائن. وينطبق الشيء نفسه إذا كنت ترغب في استخدام كائن مخصص كمفتاح في الخريطة.

وهذا أمر بالغ الأهمية كما رأيت الكثير من الناس نقع في خطأ في الممارسة إضافة كائنات مخصصة لمجموعات أو خرائط دون تجاوز يساوي () وشفرة التجزئة (). لهذا السبب يمكن أن يكون غدرا خاصة هو أن المترجم لا يشكو ويمكنك في نهاية المطاف مع كائنات متعددة التي تحتوي على نفس البيانات ولكن لديها مراجع مختلفة في مجموعة لا يسمح التكرارات.

وعلى سبيل المثال إذا كان لديك الفول بسيط يدعى NameBean مع سمة سلسلة واحدة 'اسم'، هل يمكن بناء مثيلين من NameBean (على سبيل المثال NAME1 و NAME2)، مع كل نفس 'اسم' قيمة السمة (على سبيل المثال "أليس" ). هل يمكن بعد ذلك إضافة كل NAME1 و NAME2 إلى مجموعة وان يكون حجم مجموعة 2 بدلا من حجم 1 وهذا ما هو المقصود. وبالمثل إذا كان لديك خريطة مثل خريطة من أجل رسم خريطة للفول الاسم إلى بعض وجوه الآخرين، وعليك أولا تعيين NAME1 إلى سلسلة "الأول" وبعد تعيين NAME2 إلى سلسلة "الثاني" سيكون لديك كل من أزواج / قيمة المفتاح في خريطة (على سبيل المثال NAME1 -> "أولا"، NAME2 -> "الثاني"). حتى عندما كنت تفعل خريطة بحث فإنه سيعود قيمة تعيينها إلى المرجع الدقيق الذي تمر فيه، وهو إما NAME1، NAME2، أو إشارة أخرى مع اسم "أليس" التي سيعود فارغة.

وهنا مثال ملموس يسبقه إخراج تشغيله:

وإخراج:

Adding duplicates to a map (bad):
Result of map.get(bean1):first
Result of map.get(bean2):second
Result of map.get(new NameBean("Alice"): null

Adding duplicates to a map (good):
Result of map.get(bean1):second
Result of map.get(bean2):second
Result of map.get(new ImprovedNameBean("Alice"): second

والرمز:

// This bean cannot safely be used as a key in a Map
public class NameBean {
    private String name;
    public NameBean() {
    }
    public NameBean(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return name;
    }
}

// This bean can safely be used as a key in a Map
public class ImprovedNameBean extends NameBean {
    public ImprovedNameBean(String name) {
        super(name);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if(obj == null || getClass() != obj.getClass()) {
            return false;
        }
        return this.getName().equals(((ImprovedNameBean)obj).getName());
    }
    @Override
    public int hashCode() {
        return getName().hashCode();
    }
}

public class MapDuplicateTest {
    public static void main(String[] args) {
        MapDuplicateTest test = new MapDuplicateTest();
        System.out.println("Adding duplicates to a map (bad):");
        test.withDuplicates();
        System.out.println("\nAdding duplicates to a map (good):");
        test.withoutDuplicates();
    }
    public void withDuplicates() {
        NameBean bean1 = new NameBean("Alice");
        NameBean bean2 = new NameBean("Alice");

        java.util.Map<NameBean, String> map
                = new java.util.HashMap<NameBean, String>();
        map.put(bean1, "first");
        map.put(bean2, "second");
        System.out.println("Result of map.get(bean1):"+map.get(bean1));
        System.out.println("Result of map.get(bean2):"+map.get(bean2));
        System.out.println("Result of map.get(new NameBean(\"Alice\"): "
                + map.get(new NameBean("Alice")));
    }
    public void withoutDuplicates() {
        ImprovedNameBean bean1 = new ImprovedNameBean("Alice");
        ImprovedNameBean bean2 = new ImprovedNameBean("Alice");

        java.util.Map<ImprovedNameBean, String> map
                = new java.util.HashMap<ImprovedNameBean, String>();
        map.put(bean1, "first");
        map.put(bean2, "second");
        System.out.println("Result of map.get(bean1):"+map.get(bean1));
        System.out.println("Result of map.get(bean2):"+map.get(bean2));
        System.out.println("Result of map.get(new ImprovedNameBean(\"Alice\"): "
                + map.get(new ImprovedNameBean("Alice")));
    }
}

المساواة الأساسية في المنطق (انظر قانون الهوية) ، وليس هناك الكثير من البرامج يمكنك الاستغناء عنه.أما بالنسبة لمقارنة حالات الفئات التي تكتب, هذا متروك لكم.إذا كنت بحاجة إلى أن تكون قادرة على العثور عليها في مجموعات أو استخدامها كأدوات في خرائط, سوف تحتاج المساواة الشيكات.

إذا كنت قد كتبت أكثر من بضع بديهي المكتبات في جاوة ، عليك أن تعرف أن المساواة من الصعب الحصول على حق, خصوصا عندما يكون إلا أدوات في الصدر equals و hashCode.المساواة ينتهي بإحكام إلى جانب الطبقة الهرمية ، مما يجعل هشة رمز.ما هو أكثر من ذلك ، أي نوع التحقق يتم توفيرها منذ هذه الأساليب فقط تأخذ المعلمات من نوع الكائن.

هناك طريقة لجعل المساواة التدقيق (و تجزئة) أقل عرضة للخطأ أكثر من نوع آمن.في وظيفية جافا مكتبة, سوف تجد Equal<A> (و المقابلة Hash<A>) حيث المساواة تنفصل في فئة واحدة.وقد أساليب التلحين Equal حالات الفئات الخاصة بك من القائمة الحالات ، وكذلك الأغلفة مجموعات ، Iterables, HashMap, ، HashSet, أن استخدام Equal<A> و Hash<A> بدلا من equals و hashCode.

أفضل ما في هذا النهج هو أنه لا يمكن أن ننسى أبدا أن أكتب يساوي و تجزئة الأسلوب عندما دعا.نوع النظام سوف تساعدك على تذكر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top