ปัญหาใดที่ควรพิจารณาเมื่อลบล้างเท่ากับและ hashCode ใน Java


617

จะต้องพิจารณาประเด็น / ข้อผิดพลาดใดบ้างเมื่อเอาชนะequalsและhashCode?

คำตอบ:


1439

ทฤษฎี (สำหรับนักกฎหมายด้านภาษาและความโน้มเอียงทางคณิตศาสตร์):

equals()( Javadoc ) ต้องกำหนดความสมดุล (มันจะต้องสะท้อน , สมมาตรและสกรรมกริยา ) นอกจากนี้มันจะต้องสอดคล้องกัน (หากวัตถุไม่ได้ถูกแก้ไขแล้วมันจะต้องกลับมาเป็นค่าเดิม) นอกจากนี้o.equals(null)ต้องส่งคืนค่าเท็จเสมอ

hashCode()( javadoc ) จะต้องสอดคล้องกัน (หากวัตถุนั้นไม่ได้รับการแก้ไขในแง่ของequals()มันจะต้องส่งกลับค่าเดียวกัน)

ความสัมพันธ์ระหว่างสองวิธีคือ

เมื่อใดก็ตามที่a.equals(b)แล้วจะต้องเป็นเช่นเดียวกับa.hashCode()b.hashCode()

ในทางปฏิบัติ:

หากคุณแทนที่หนึ่งแล้วคุณควรแทนที่อีก

ใช้ชุดเดียวกันของเขตข้อมูลที่คุณใช้ในการคำนวณการคำนวณequals()hashCode()

ใช้คลาสตัวช่วยที่ยอดเยี่ยมEqualsBuilderและHashCodeBuilderจากไลบรารีApache Commons Lang ตัวอย่าง:

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 () ของวัตถุสำคัญที่คุณใส่ลงในคอลเลกชันจะไม่เปลี่ยนแปลงในขณะที่วัตถุอยู่ในคอลเลกชัน วิธีกันกระสุนเพื่อให้แน่ใจว่าสิ่งนี้จะทำให้กุญแจของคุณไม่เปลี่ยนรูปซึ่งมีประโยชน์อื่นเช่นกัน


12
จุดเพิ่มเติมเกี่ยวกับ appendSuper (): คุณควรใช้มันใน hashCode () และ equals () ถ้าหากคุณต้องการสืบทอดพฤติกรรมความเสมอภาคของซูเปอร์คลาส ตัวอย่างเช่นหากคุณได้รับโดยตรงจากวัตถุจะไม่มีจุดเพราะวัตถุทั้งหมดจะแตกต่างกันตามค่าเริ่มต้น
Antti Kissaniemi

312
คุณสามารถรับ Eclipse เพื่อสร้างสองวิธีสำหรับคุณ: ที่มา> สร้าง hashCode () และ equals ()
Rok Strniša

27
เช่นเดียวกันกับ Netbeans: developmentality.wordpress.com/2010/08/24/…
seinecle

6
@Darthenius Eclipse ที่สร้างขึ้นเท่ากับใช้ getClass () ซึ่งอาจทำให้เกิดปัญหาในบางกรณี (ดูรายการ Java ที่มีประสิทธิภาพ 8)
AndroidGecko

7
การตรวจสอบ null ครั้งแรกไม่จำเป็นเนื่องจากข้อเท็จจริงที่instanceofส่งกลับค่า false ถ้าตัวถูกดำเนินการแรกเป็นโมฆะ (Effective Java อีกครั้ง)
izaban

295

มีปัญหาบางอย่างที่ควรสังเกตหากคุณกำลังจัดการกับคลาสที่ยังคงใช้ Object-Relationship Mapper (ORM) เช่น Hibernate ถ้าคุณไม่คิดว่ามันซับซ้อนอย่างไม่มีเหตุผลแล้ว!

วัตถุที่โหลดขี้เกียจเป็น subclasses

หากวัตถุของคุณยังคงใช้ 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เป็นเพียงสิ่งเดียวที่จะทำงานได้อย่างถูกต้อง

วัตถุที่โหลดขี้เกียจมีช่องว่าง

ORMs มักใช้ getters เพื่อบังคับให้โหลดวัตถุที่โหลดแบบ lazy ซึ่งหมายความว่าperson.nameจะเป็นnullถ้าpersonมีการโหลดที่ขี้เกียจแม้ว่าperson.getName()บังคับให้โหลดและส่งคืน "John Doe" จากประสบการณ์ของผมพืชผลนี้บ่อยขึ้นhashCode()และequals()มากขึ้น

หากคุณกำลังจัดการกับการออมให้แน่ใจว่าจะมักจะใช้ getters และไม่เคยอ้างอิงในสนามและhashCode()equals()

การบันทึกวัตถุจะเปลี่ยนสถานะของมัน

วัตถุถาวรมักจะใช้idเขตข้อมูลเพื่อเก็บกุญแจของวัตถุ ฟิลด์นี้จะถูกอัปเดตโดยอัตโนมัติเมื่อมีการบันทึกวัตถุเป็นครั้งแรก hashCode()อย่าใช้ข้อมูลประจำตัวประชาชนใน แต่คุณสามารถใช้มันequals()ได้

รูปแบบที่ฉันมักจะใช้คือ

if (this.getId() == null) {
    return this == other;
}
else {
    return this.getId().equals(other.getId());
}

แต่คุณไม่สามารถรวมในgetId() hashCode()ถ้าคุณทำเมื่อวัตถุยังคงอยู่การhashCodeเปลี่ยนแปลงของวัตถุ หากวัตถุอยู่ใน a HashSetคุณจะ "ไม่เคย" ค้นหามันอีก

ในของฉันPersonตัวอย่างเช่นผมอาจจะใช้getName()สำหรับการhashCodeและgetId()บวกgetName()(เพียงสำหรับความหวาดระแวง) equals()สำหรับ มันโอเคถ้ามีความเสี่ยงของการ "ชน" สำหรับบางคนแต่ไม่เคยถูกสำหรับhashCode()equals()

hashCode() ควรใช้ชุดย่อยของคุณสมบัติที่ไม่เปลี่ยนแปลง equals()


2
@ Johannes Brodwall: ฉันไม่เข้าใจSaving an object will change it's state! hashCodeจะต้องกลับมาintดังนั้นคุณจะใช้getName()อย่างไร? คุณสามารถยกตัวอย่างให้กับคุณhashCode
jimmybondy

@jimmybondy: getName จะส่งคืนวัตถุ String ซึ่งมี hashCode ซึ่งสามารถใช้ได้
mateusz.fiolka

85

obj.getClass() != getClass()ชี้แจงเกี่ยวกับ

คำสั่งนี้เป็นผลมาจากequals()การเป็นมรดกที่ไม่เป็นมิตร JLS (สเปคภาษา Java) ระบุว่าถ้า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 และหลักการการทดแทน Liskov 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() ก็จะเรียกเช่นกัน


2
คุณสามารถสร้างสมมาตรแบบสมมาตรได้ด้วยวิธีนี้ (หากเปรียบเทียบวัตถุ superclass กับวัตถุ subclass ให้ใช้เท่ากับของ subclass เสมอ) ถ้า (obj.getClass ()! = this.getClass () && obj.getClass () isInstance (this) ) ส่งคืน obj.equals (นี่);
pihentagy

5
@pihentagy - ฉันจะได้รับ stackoverflow เมื่อคลาสการใช้งานไม่ได้แทนที่เมธอด equals ไม่สนุก.
Ran Biron

2
คุณจะไม่ได้รับ stackoverflow หากวิธีการเท่ากับไม่ถูกแทนที่คุณจะเรียกรหัสเดิมอีกครั้ง แต่เงื่อนไขการเรียกซ้ำจะเป็นเท็จเสมอ!
Jacob Raihle

@pihentagy: มันจะทำงานยังไงถ้ามันมีสองคลาสที่ได้มาต่างกัน? หาก a ThingWithOptionSetAสามารถเท่ากับที่Thingระบุไว้ว่าตัวเลือกพิเศษทั้งหมดมีค่าเริ่มต้นและเช่นเดียวกันสำหรับ a ThingWithOptionSetBดังนั้นมันควรจะเป็นไปได้ThingWithOptionSetAที่จะเปรียบเทียบเท่ากับ a ThingWithOptionSetBเท่านั้นหากคุณสมบัติที่ไม่ใช่ฐานทั้งหมดของวัตถุทั้งสองตรงกับค่าเริ่มต้น แต่ ฉันไม่เห็นวิธีการทดสอบของคุณ
supercat

7
ปัญหาเกี่ยวกับสิ่งนี้ก็คือมันทำลายความไวแสง ถ้าคุณเพิ่มB b2 = new B(1,99)แล้วb.equals(a) == trueและแต่a.equals(b2) == true b.equals(b2) == false
nickgrim

46

สำหรับการติดตั้งที่ง่ายต่อการสืบทอดให้ตรวจสอบโซลูชันของ Tal Cohen ฉันจะใช้วิธีการเท่ากับ () ได้อย่างถูกต้องได้อย่างไร

สรุป:

ในหนังสือคู่มือภาษาจาวาที่มีประสิทธิภาพ (แอดดิสัน - เวสลีย์, 2544) โจชัวโบลชอ้างว่า "ไม่มีทางที่จะขยายชั้นเรียนได้ทันทีและเพิ่มมุมมองใหม่ ๆ ทัลไม่เห็นด้วย

วิธีแก้ปัญหาของเขาคือการใช้ equals () โดยการเรียกอีกอย่างที่ไม่สมมาตร blindlyEquals () ทั้งสองวิธี blindlyEquals () ถูกแทนที่โดยคลาสย่อยเท่ากับ () จะถูกสืบทอดและไม่เคยถูกแทนที่

ตัวอย่าง:

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);
    }
}

โปรดทราบว่าเท่ากับ () ต้องทำงานข้ามลำดับชั้นการสืบทอดหากหลักการการทดแทน Liskovนั้นเป็นที่น่าพอใจ


10
ดูที่วิธี canEqual อธิบายที่นี่ - หลักการเดียวกันทำให้ทั้งสองวิธีทำงานได้ แต่ด้วย canEqual คุณไม่ได้เปรียบเทียบฟิลด์เดียวกันสองครั้ง (ด้านบน px == this.x จะถูกทดสอบทั้งสองทิศทาง): artima.com /lejava/articles/equality.html
Blaisorblade

2
ไม่ว่าในกรณีใดฉันไม่คิดว่านี่เป็นความคิดที่ดี มันทำให้สัญญา Equals สับสนโดยไม่จำเป็น - คนที่ใช้พารามิเตอร์ Point สองตัวคือ a และ b จะต้องตระหนักถึงความเป็นไปได้ที่ a.getX () == b.getX () และ a.getY () == b.getY () สามารถเป็นจริงได้ แต่ a.equals (b) และ b.equals (a) ทั้งคู่เป็นเท็จ (ถ้าเพียงคนเดียวคือ ColorPoint)
เควิน

โดยพื้นฐานแล้วนี่เป็นสิ่งที่ชอบif (this.getClass() != o.getClass()) return falseแต่มีความยืดหยุ่นในการที่จะคืนค่าเท็จถ้าคลาสที่ได้รับมารบกวนการแก้ไขเท่ากับ นั่นถูกต้องใช่ไหม?
Aleksandr Dubinsky

31

ยังคงประหลาดใจที่ไม่มีใครแนะนำไลบรารีฝรั่งสำหรับสิ่งนี้

 //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());
    }

23
java.util.Objects.hash () และ java.util.Objects.equals () เป็นส่วนหนึ่งของ Java 7 (เปิดตัวในปี 2011) ดังนั้นคุณไม่จำเป็นต้องใช้ Guava
เฮอร์แมน

1
แน่นอน แต่คุณควรหลีกเลี่ยงเพราะ Oracle ไม่ได้ให้บริการอัพเดตสาธารณะอีกต่อไปสำหรับ Java 6 (เป็นกรณีนี้ตั้งแต่เดือนกุมภาพันธ์ 2013)
เฮอร์แมน

6
thisในความthis.getDate()หมายของคุณไม่มีอะไร (นอกเหนือจากความยุ่งเหยิง)
Steve Kuo

1
ของคุณ "ไม่ instanceof" if (!(otherObject instanceof DateAndPattern)) {การแสดงออกต้องมีวงเล็บพิเศษ: เห็นด้วยกับเฮอร์นันและสตีฟคูโอะ (แม้ว่าจะเป็นเรื่องของความชอบส่วนตัว) แต่ +1 อย่างไรก็ตาม
Amos M. Carpenter

26

มีสองวิธีในคลาส super เป็น 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

มีความสุข! @. @


ขออภัย แต่ฉันไม่เข้าใจคำสั่งนี้เกี่ยวกับวิธีการ hashCode: มันไม่ถูกต้องหากมันใช้ตัวแปรมากกว่าจำนวนเท่ากับ () แต่ถ้าฉันโค้ดที่มีตัวแปรมากกว่านี้โค้ดของฉันก็จะคอมไพล์ ทำไมมันไม่ถูกกฎหมาย?
Adryr83

19

มีสองวิธีในการตรวจสอบความเท่าเทียมกันในชั้นเรียนของคุณก่อนที่จะตรวจสอบความเท่าเทียมกันของสมาชิกและฉันคิดว่าทั้งสองมีประโยชน์ในสถานการณ์ที่เหมาะสม

  1. ใช้instanceofโอเปอเรเตอร์
  2. this.getClass().equals(that.getClass())ใช้

ฉันใช้ # 1 ในการfinalใช้งานที่เท่าเทียมกันหรือเมื่อใช้งานอินเทอร์เฟซที่กำหนดอัลกอริทึมสำหรับเท่ากับ (เช่น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;
  }

}

1
+1 สำหรับสิ่งนี้ getClass () และ instanceof ไม่เป็นยาครอบจักรวาลและนี่เป็นคำอธิบายที่ดีเกี่ยวกับวิธีการเข้าหาทั้งคู่ อย่าคิดว่ามีเหตุผลที่จะไม่ทำเช่นนี้ getClass () == that.getClass () แทนที่จะใช้เท่ากับ ()
Paul Cantrell

มีปัญหาอย่างหนึ่งสำหรับสิ่งนี้ คลาสที่ไม่ระบุชื่อซึ่งไม่ได้เพิ่มแง่มุมใด ๆ หรือไม่แทนที่เมธอดเท่ากับจะทำให้การตรวจสอบ getClass ล้มเหลวแม้ว่ามันจะควรเท่ากันก็ตาม
steinybot

@Steiny ไม่ชัดเจนสำหรับฉันว่าวัตถุประเภทต่างๆควรเท่ากัน; ฉันกำลังคิดถึงการใช้งานอินเทอร์เฟซที่แตกต่างกันเป็นคลาสที่ไม่ระบุชื่อทั่วไป คุณสามารถยกตัวอย่างเพื่อสนับสนุนหลักฐานของคุณได้หรือไม่?
erickson

MyClass a = new MyClass (123); MyClass b = new MyClass (123) {// แทนที่บางวิธี}; // a.equals (b) เป็นเท็จเมื่อใช้ this.getClass (). เท่ากับ (that.getClass ())
steinybot

1
@Steiny ถูกต้อง ตามที่ควรในกรณีส่วนใหญ่โดยเฉพาะอย่างยิ่งหากวิธีการแทนที่มากกว่าการเพิ่ม พิจารณาตัวอย่างของฉันด้านบน ถ้ามันไม่ได้finalและcompareTo()วิธีการที่ถูกแทนที่เพื่อเรียงลำดับการจัดเรียงอินสแตนซ์ของคลาสย่อยและซูเปอร์คลาสไม่ควรพิจารณาเท่ากัน เมื่อวัตถุเหล่านี้ถูกนำมาใช้ร่วมกันในต้นไม้คีย์ที่ "เท่าเทียมกัน" ตามการinstanceofใช้งานอาจไม่สามารถค้นหาได้
erickson

16

สำหรับเท่ากับมองเข้าไปในความลับของเท่ากับโดยAngelika แลงเกอร์ ฉันรักมันมาก เธอยังเป็นคำถามที่พบบ่อยที่ดีเกี่ยวกับยาสามัญในชวา ดูบทความอื่น ๆ ของเธอที่นี่ (เลื่อนลงไปที่ "คอร์ Java") ซึ่งเธอยังไปกับ Part-2 และ "การเปรียบเทียบประเภทผสม" ขอให้สนุกกับการอ่านมัน!


11

equals () วิธีการที่ใช้ในการกำหนดความเท่าเทียมกันของวัตถุทั้งสอง

ค่า int ของ 10 จะเท่ากับ 10 เสมอ แต่เมธอด equals () นี้เกี่ยวกับความเท่าเทียมกันของวัตถุทั้งสอง เมื่อเราพูดวัตถุมันจะมีคุณสมบัติ ในการตัดสินใจเกี่ยวกับความเสมอภาคคุณสมบัติเหล่านั้นจะถูกพิจารณา ไม่จำเป็นว่าจะต้องคำนึงถึงคุณสมบัติทั้งหมดเพื่อพิจารณาความเสมอภาคและด้วยความเคารพต่อคำจำกัดความของชั้นเรียนและบริบทที่สามารถตัดสินใจได้ จากนั้นวิธี equals () สามารถแทนที่ได้

เราควรแทนที่เมธอด hashCode () ทุกครั้งที่เราแทนที่เมธอด equals () ถ้าไม่จะเกิดอะไรขึ้น หากเราใช้แฮชเทเบิลในแอปพลิเคชันของเรามันจะไม่ทำงานตามที่คาดไว้ เนื่องจาก hashCode ถูกใช้ในการพิจารณาความเท่าเทียมกันของค่าที่เก็บไว้มันจะไม่ส่งคืนค่าที่สอดคล้องกันที่เหมาะสมสำหรับคีย์

การใช้งานเริ่มต้นที่กำหนดคือ hashCode () วิธีการในชั้นวัตถุใช้ที่อยู่ภายในของวัตถุและแปลงเป็นจำนวนเต็มและส่งกลับ

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

7

เรามีเหตุผล:

a.getClass().equals(b.getClass()) && a.equals(b)a.hashCode() == b.hashCode()

แต่ไม่ใช่ในทางกลับกัน!


6

หนึ่ง gotcha ที่ฉันได้พบคือที่ซึ่งวัตถุสองชิ้นมีการอ้างอิงถึงกันและกัน (ตัวอย่างหนึ่งคือความสัมพันธ์ของผู้ปกครอง / เด็กที่มีวิธีการอำนวยความสะดวกในผู้ปกครองเพื่อรับลูกทั้งหมด)
สิ่งต่าง ๆ เหล่านี้เป็นเรื่องธรรมดาเมื่อทำการจับคู่ไฮเบอร์เนต

หากคุณรวมทั้งสองด้านของความสัมพันธ์ใน hashCode หรือการทดสอบเท่ากับคุณสามารถเข้าสู่วนซ้ำแบบซ้ำซึ่งจบลงใน StackOverflowException
วิธีที่ง่ายที่สุดคือไม่รวมคอลเลกชัน getChildren ไว้ในเมธอด


5
ผมคิดว่าทฤษฎีพื้นฐานที่นี่คือการแยกแยะความแตกต่างระหว่างคุณลักษณะ , มวลและassociatinosของวัตถุ asssociationsequals()ไม่ควรมีส่วนร่วมใน หากนักวิทยาศาสตร์บ้าสร้างซ้ำของฉันเราจะเทียบเท่า แต่เราจะไม่มีพ่อคนเดียวกัน
Raedwald
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.