ทำไมฉันต้องแทนที่เมธอด equals และ hashCode ใน Java


383

เมื่อเร็ว ๆ นี้ฉันอ่านเอกสารสำหรับนักพัฒนานี้ นี้

เอกสารทั้งหมดเกี่ยวกับการกำหนดhashCode()และequals()มีประสิทธิภาพและถูกต้อง แต่ฉันไม่สามารถคิดออกว่าทำไมเราต้องแทนที่ทั้งสองวิธี

ฉันจะตัดสินใจใช้วิธีการเหล่านี้อย่างมีประสิทธิภาพได้อย่างไร


4
มีบทความดีๆอยู่สองรายการที่ programming.guide อธิบายตรงนี้: เมื่อใดที่ฉันควรแทนที่เท่ากับ และทำไมคุณควรแทนที่ hashCode เมื่อเอาชนะเท่ากับ (คำเตือนคำตอบที่ยอมรับนั้นผิดจริง)
aioobe

Case Override เท่ากับ: สองวัตถุเดียวกันจะมี hashcode ที่แตกต่างกัน = วัตถุเดียวกันไปในที่เก็บข้อมูลที่ต่างกัน (การทำซ้ำ) Case Override hashcode เท่านั้น: สองวัตถุเดียวกันจะมี hashcode เดียวกัน = object เดียวกันทำงานในที่เก็บข้อมูลเดียวกัน (การทำซ้ำ)
VdeX

คำตอบ:


524

Joshua Bloch กล่าวใน Java ที่มีประสิทธิภาพ

คุณจะต้องแทนที่ hashCode () ในทุก ๆ คลาสที่แทนที่ () การล้มเหลวในการทำเช่นนั้นจะส่งผลให้มีการละเมิดสัญญาทั่วไปสำหรับ Object.hashCode () ซึ่งจะป้องกันไม่ให้คลาสของคุณทำงานอย่างถูกต้องเมื่อใช้ร่วมกับคอลเลกชันที่ใช้แฮชทั้งหมดรวมถึง HashMap, HashSet และ Hashtable

ลองทำความเข้าใจกับตัวอย่างของสิ่งที่จะเกิดขึ้นหากเราแทนที่equals()โดยไม่มีการแทนที่hashCode()และพยายามใช้Mapและพยายามที่จะใช้

สมมติว่าเรามีคลาสเช่นนี้และวัตถุสองอย่างMyClassนั้นเท่ากันถ้าimportantFieldมันเท่ากัน (ด้วยhashCode()และequals()สร้างโดย eclipse)

public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

แทนที่เท่านั้น equals

ถ้าเพียง แต่equalsจะถูกแทนที่แล้วเมื่อคุณโทรmyMap.put(first,someValue)ประสงค์กัญชาแรกที่จะถังบางอย่างและเมื่อคุณเรียกmyMap.put(second,someOtherValue)มันจะสับถังอื่น ๆ บางคน (ที่พวกเขามีที่แตกต่างกันhashCode) ดังนั้นแม้ว่าพวกเขาจะเท่ากันเนื่องจากพวกเขาไม่ได้แฮชไปยังที่เก็บข้อมูลเดียวกันแผนที่ไม่สามารถรู้ได้และพวกเขาทั้งสองอยู่ในแผนที่


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

แทนที่เท่านั้น hashCode

ลองนึกภาพคุณมีสิ่งนี้

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

หากคุณแทนที่เท่านั้นhashCodeเมื่อคุณเรียกmyMap.put(first,someValue)ใช้จะต้องใช้ก่อนคำนวณhashCodeและเก็บไว้ในที่ฝากข้อมูลที่กำหนด จากนั้นเมื่อคุณเรียกmyMap.put(second,someOtherValue)มันควรแทนที่อันดับหนึ่งด้วยอันดับที่สองตามเอกสารประกอบของแผนที่เพราะเท่ากัน (ตามความต้องการทางธุรกิจ)

แต่ปัญหาคือว่าเท่าเทียมกันไม่ได้ถูกนิยามใหม่ดังนั้นเมื่อ hashes แผนที่secondและ iterates ผ่านถังมองถ้ามีวัตถุkดังกล่าวว่าsecond.equals(k)เป็นความจริงมันจะไม่พบใด ๆ ที่เป็นจะเป็นsecond.equals(first)false

หวังว่ามันชัดเจน


5
คุณช่วยอธิบายเพิ่มเติมอีกหน่อยได้ไหมในกรณีที่สองทำไมวัตถุที่สองต้องไปในที่เก็บข้อมูลอื่น
Hussain Akhtar Wahid 'Ghouri'

57
ฉันไม่ชอบคำตอบนี้เพราะมันแสดงให้เห็นว่าคุณไม่สามารถแทนที่ hashCode () โดยไม่ต้องลบล้างเท่ากับ () ซึ่งไม่จริง คุณพูดว่าโค้ดตัวอย่างของคุณ (ส่วน "แทนที่เฉพาะ hashCode") จะไม่ทำงานเพราะคุณกำหนดวัตถุสองชิ้นให้เท่ากัน แต่ - ขอโทษ - คำจำกัดความนี้อยู่ในหัวของคุณเท่านั้น ในตัวอย่างแรกของคุณคุณมีวัตถุที่ไม่เท่ากันสองตัวที่มี hashCode เดียวกันและนั่นถูกกฎหมายอย่างสมบูรณ์ ดังนั้นเหตุผลที่คุณต้องการแทนที่เท่ากับ () ไม่ใช่เพราะคุณได้ลบล้าง hashCode () แล้ว แต่เพราะคุณต้องการย้ายคำจำกัดความ "เท่ากับ" ของคุณจากหัวไปยังรหัส
user2543253

11
if you think you need to override one, then you need to override both of themมันผิด. คุณต้องลบล้างhashCodeหากการแทนที่ชั้นเรียนของคุณequalsแต่การย้อนกลับไม่เป็นความจริง
akhil_mittal

4
ฉันคิดว่ามันก็โอเคที่จะแทนที่ hashCode ()โดยไม่แทนที่เท่ากับ () เช่นกัน นอกจากนี้ยังเป็นสิ่งที่เขียนในJava ที่มีประสิทธิภาพ : books.google.fr/…
จอห์นนี่

2
@PhantomReference โปรดทราบว่าเฉพาะการเอาชนะเท่านั้นที่equalsจะเป็นการละเมิดสัญญาที่สะกดใน javadoc ของObject: "หากวัตถุสองรายการมีค่าเท่ากันตามequals(Object)วิธีการแล้วเรียกhashCodeวิธีการในแต่ละวัตถุทั้งสองนั้นจะต้องสร้างผลลัพธ์จำนวนเต็มที่เหมือนกัน" แน่นอนว่าไม่ใช่ทุกส่วนของสัญญาทั้งหมดที่ใช้ในรหัสทั้งหมด แต่ก็ยังพูดอย่างเป็นทางการว่าเป็นการละเมิดและฉันคิดว่ามันเป็นข้อผิดพลาดที่รอให้เกิดขึ้น
aioobe

264

คอลเล็กชันเช่นHashMapและHashSetใช้ค่าแฮชโค้ดของวัตถุเพื่อกำหนดวิธีการจัดเก็บภายในคอลเลกชันและแฮชโค้ดอีกครั้งเพื่อค้นหาวัตถุในคอลเลกชัน

การดึงข้อมูลการแฮชเป็นกระบวนการสองขั้นตอน:

  1. ค้นหาที่เก็บข้อมูลที่ถูกต้อง (โดยใช้ hashCode() )
  2. ค้นหาที่เก็บข้อมูลสำหรับองค์ประกอบที่ถูกต้อง (โดยใช้equals())

นี่เป็นตัวอย่างเล็ก ๆ ว่าทำไมเราควรแทนที่equals()และhashcode()และ

พิจารณาEmployeeคลาสที่มีสองฟิลด์: อายุและชื่อ

public class Employee {

    String name;
    int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

ตอนนี้สร้างคลาสใส่EmployeeวัตถุลงในHashSetและทดสอบว่าวัตถุนั้นมีอยู่หรือไม่

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

มันจะพิมพ์ต่อไปนี้:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

ตอนนี้hashcode()วิธีuncomment ดำเนินการเดียวกันและผลลัพธ์จะเป็น:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

ตอนนี้คุณสามารถดูได้ว่าทำไมถ้าวัตถุสองชิ้นถือว่าเท่ากันhashcode s ต้องเท่ากัน? ไม่เช่นนั้นคุณจะไม่สามารถค้นหาวัตถุได้เนื่องจากวิธีการแฮชโค้ดเริ่มต้น ในคลาส Object มักจะมีหมายเลขที่ไม่ซ้ำกันสำหรับแต่ละวัตถุแม้ว่าequals()วิธีนั้นจะถูกแทนที่ด้วยวิธีการที่วัตถุสองชิ้นหรือมากกว่านั้นถือว่าเท่ากัน . มันไม่สำคัญว่าวัตถุจะเท่ากันถ้าแฮชโค้ดของมันไม่สะท้อนสิ่งนั้น ดังนั้นอีกครั้ง: หากวัตถุสองอันเท่ากัน hashcode s จะต้องเท่ากัน


4
ตัวอย่างที่สมบูรณ์แบบ แสดงให้เห็นถึงความแตกต่างอย่างชัดเจน!
coderpc

3
nice อธิบาย @rajeev
VdeX

2
@VikasVerma เท่ากับวัตถุจะมีแฮชโค้ดที่เท่ากันไม่ได้หมายความว่าวัตถุที่ไม่เท่ากันจะมีแฮชโค้ดที่ไม่เท่ากัน เกิดอะไรขึ้นถ้าวัตถุแตกต่างกันจริง แต่แฮชโค้ดของพวกเขาเหมือนกัน?
Ravi

1
อธิบายได้ดีมาก :)
Rahul

4
คำตอบที่ดีกว่ามากแล้วคำตอบที่ยอมรับ! ขอขอบคุณ
driftwood

50

คุณจะต้องแทนที่ hashCode () ในทุก ๆ คลาสที่แทนที่ () การล้มเหลวในการทำเช่นนั้นจะส่งผลให้มีการละเมิดสัญญาทั่วไปสำหรับ Object.hashCode () ซึ่งจะป้องกันไม่ให้คลาสของคุณทำงานอย่างถูกต้องเมื่อใช้ร่วมกับคอลเลกชันที่ใช้แฮชทั้งหมดรวมถึง HashMap, HashSet และ Hashtable


   จากJava ที่มีประสิทธิภาพโดย Joshua Bloch

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

คำตอบที่ดีที่สุดสำหรับคำถามของคุณเกี่ยวกับวิธีการที่จะใช้วิธีการเหล่านี้ได้อย่างมีประสิทธิภาพจะแนะนำให้คุณอ่านบทที่ 3 ของการมีผลบังคับใช้ Java


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

1
ในกรณีที่ซับซ้อนมากขึ้นคุณจะไม่มีทางรู้ว่าคอลเลกชันที่คุณใช้กำลังใช้แฮชอยู่ดังนั้นอย่าอยู่ห่างจาก "มันไม่สำคัญว่าคุณยังไม่ได้ติดตั้ง hashCode ()"
Victor Sergienko

1
ฉันสามารถแทนที่ hashCode () โดยไม่ต้องลบล้างเท่ากับ () ได้หรือไม่?
Johnny

@ สถานะใช่ตรงข้ามกับคำตอบที่ยอมรับ ดูคำอธิบายในส่วนที่สองของบทความนี้: ทำไมคุณควรแทนที่ hashCode เสมอเมื่อเอาชนะเท่ากับ
aioobe

22

พูดง่ายๆคือวิธีเท่ากับในการตรวจสอบวัตถุเพื่อความเท่าเทียมในการอ้างอิงโดยที่อินสแตนซ์สองชั้นของคลาสของคุณอาจยังคงมีความหมายเท่ากันเมื่อคุณสมบัติเท่ากัน นี้เป็นตัวอย่างที่สำคัญเมื่อวางวัตถุของคุณลงในภาชนะที่ใช้เท่ากับและแฮชโค้ดเช่นHashMapและชุด สมมติว่าเรามีชั้นเรียนเช่น:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

เราสร้างสองกรณีด้วยรหัสเดียวกัน:

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

เราจะได้รับ:

  • a.equals (b) เป็นเท็จเพราะมีสองอินสแตนซ์ที่ต่างกัน
  • a.equals (a) เป็นจริงเนื่องจากมันเป็นอินสแตนซ์เดียวกัน
  • b.equals (b) เป็นจริงเนื่องจากมันเป็นอินสแตนซ์เดียวกัน

แก้ไข? บางทีถ้านี่คือสิ่งที่คุณต้องการ แต่สมมุติว่าเราต้องการให้อ็อบเจกต์ที่มีไอดีเดียวกันเป็นวัตถุเดียวกัน เราแทนที่ค่าเท่ากับ (และ hashcode):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

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

สำหรับการใช้งานเท่ากับและ hashcode ฉันสามารถแนะนำให้ใช้วิธีการช่วยเหลือของ Guava


20

ตัวตนไม่เท่าเทียมกัน

  • เท่ากับ==ตัวตนของผู้ทดสอบ
  • equals(Object obj) วิธีการเปรียบเทียบการทดสอบความเท่าเทียมกัน (เช่นเราจำเป็นต้องบอกความเท่าเทียมกันโดยเอาชนะวิธี)

ทำไมฉันต้องแทนที่เมธอด equals และ hashCode ใน Java

ก่อนอื่นเราต้องเข้าใจการใช้วิธีการเท่ากับ

เพื่อความแตกต่างของอัตลักษณ์ระหว่างสองวัตถุเราจำเป็นต้องแทนที่เมธอดเท่ากับ

ตัวอย่างเช่น:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

ตอนนี้วิธี hashCode สามารถเข้าใจได้ง่าย

hashCode ผลิตจำนวนเต็มในการสั่งซื้อไปยังวัตถุที่เก็บไว้ในโครงสร้างข้อมูลเช่นHashMap , HashSet

สมมติว่าเรามีวิธีการแทนที่เท่ากับCustomerข้างต้น

customer1.equals(customer2);  // returns true by our own logic

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

  • อินสแตนซ์ที่ไม่เท่ากันอาจมีแฮชโค้ดเดียวกัน
  • อินสแตนซ์ที่เท่ากันควรส่งคืนแฮชโค้ดเดียวกัน

3
นี่คือสิ่งที่ฉันกำลังมองหาตั้งแต่ 1 ชั่วโมงที่ผ่านมา เพื่อนที่ดีเลิศ
Adnan

13

ตกลงให้ฉันอธิบายแนวคิดด้วยคำพูดที่ง่ายมาก

ประการแรกจากมุมมองที่กว้างขึ้นเรามีคอลเลกชันและ hashmap เป็นหนึ่งในโครงสร้างข้อมูลในคอลเลกชัน

เพื่อให้เข้าใจว่าทำไมเราต้องแทนที่ทั้งวิธีเท่ากับและ hashcode ถ้าจำเป็นต้องเข้าใจก่อนว่า hashmap คืออะไรและทำอะไร

hashmap เป็นโครงสร้างข้อมูลที่เก็บคู่ค่าคีย์ของข้อมูลในแบบอาเรย์ ให้บอกว่า [] โดยที่แต่ละองค์ประกอบใน 'a' เป็นคู่ของค่าคีย์

นอกจากนี้แต่ละดัชนีในอาเรย์ข้างต้นสามารถเชื่อมโยงรายการดังนั้นจึงมีค่ามากกว่าหนึ่งค่าที่ดัชนีเดียว

ตอนนี้ทำไมจึงใช้ hashmap หากเราต้องค้นหาในอาร์เรย์ขนาดใหญ่แล้วค้นหาแต่ละรายการหากไม่มีประสิทธิภาพดังนั้นเทคนิคแฮชบอกเราว่าให้ประมวลผลอาร์เรย์ด้วยตรรกะและจัดกลุ่มองค์ประกอบตามตรรกะนั้นเช่น Hashing

เช่น: เรามีอาร์เรย์ 1,2,3,4,5,6,7,8,9,10,11 และเราใช้ฟังก์ชันแฮช mod mod 10 ดังนั้น 1,11 จะถูกจัดกลุ่มเข้าด้วยกัน ดังนั้นถ้าเราต้องค้นหา 11 ในอาเรย์ก่อนหน้าเราจะต้องวนซ้ำอาเรย์ทั้งหมด แต่เมื่อเราจัดกลุ่มมันเราจะ จำกัด ขอบเขตของการวนซ้ำซึ่งจะเป็นการเพิ่มความเร็ว โครงสร้างข้อมูลที่ใช้ในการจัดเก็บข้อมูลทั้งหมดข้างต้นสามารถคิดเป็นอาร์เรย์ 2d เพื่อความเรียบง่าย

ตอนนี้นอกเหนือจาก hashmap ด้านบนยังบอกด้วยว่าจะไม่เพิ่มรายการซ้ำใด ๆ ในนั้น และนี่คือเหตุผลหลักว่าทำไมเราต้องแทนที่เท่ากับและแฮชโค้ด

ดังนั้นเมื่อมันบอกว่าอธิบายการทำงานภายในของ hashmap เราจำเป็นต้องค้นหาว่า hashmap นั้นมีวิธีการอย่างไรและมันทำตามกฎข้างต้นที่ฉันอธิบายไว้ข้างต้นได้อย่างไร

ดังนั้น hashmap มีวิธีการที่เรียกว่าเป็น put (K, V) และตาม hashmap มันควรเป็นไปตามกฎข้างต้นของการกระจายอาร์เรย์อย่างมีประสิทธิภาพและไม่เพิ่มรายการที่ซ้ำกัน

ดังนั้นสิ่งที่ใส่ทำคือมันจะสร้าง hashcode สำหรับคีย์ที่กำหนดเพื่อตัดสินใจว่าดัชนีควรมีค่าใดถ้าไม่มีอะไรอยู่ในดัชนีนั้นจากนั้นค่าใหม่จะถูกเพิ่มที่นั่นถ้ามีบางสิ่งอยู่ที่นั่น ดังนั้นควรเพิ่มค่าใหม่หลังจากสิ้นสุดรายการที่ลิงก์ในดัชนีนั้น แต่จำไว้ว่าไม่ควรเพิ่มรายการที่ซ้ำกันตามพฤติกรรมที่ต้องการของ hashmap สมมุติว่าคุณมีอ็อบเจกต์ Integer สองตัว aa = 11, bb = 11 เนื่องจากทุกอ็อบเจ็กต์ที่ได้รับจากคลาสอ็อบเจ็กต์การประยุกต์ใช้ดีฟอลต์สำหรับการเปรียบเทียบสองอ็อบเจ็กต์คือมันเปรียบเทียบการอ้างอิงและไม่ใช่ค่าภายในอ็อบเจ็กต์ ดังนั้นในกรณีข้างต้นทั้งสองแม้ว่าจะมีความหมายเท่าเทียมกันจะล้มเหลวในการทดสอบความเท่าเทียมกันและความเป็นไปได้ที่วัตถุสองชนิดที่มีค่าแฮชโค้ดและค่าเดียวกันจะมีอยู่ดังนั้นจึงสร้างรายการที่ซ้ำกัน หากเราแทนที่แล้วเราสามารถหลีกเลี่ยงการเพิ่มรายการที่ซ้ำกัน คุณสามารถอ้างถึงรายละเอียดการทำงาน

import java.util.HashMap;


public class Employee {

String name;
String mobile;
public Employee(String name,String mobile) {
    this.name=name;
    this.mobile=mobile;
}

@Override
public int hashCode() {
    System.out.println("calling hascode method of Employee");
    String str=this.name;
    Integer sum=0;
    for(int i=0;i<str.length();i++){
        sum=sum+str.charAt(i);
    }
    return sum;

}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    System.out.println("calling equals method of Employee");
    Employee emp=(Employee)obj;
    if(this.mobile.equalsIgnoreCase(emp.mobile)){

        System.out.println("returning true");
        return true;
    }else{
        System.out.println("returning false");
        return false;
    }


}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    Employee emp=new Employee("abc", "hhh");
    Employee emp2=new Employee("abc", "hhh");
    HashMap<Employee, Employee> h=new HashMap<>();
    //for (int i=0;i<5;i++){
        h.put(emp, emp);
        h.put(emp2, emp2);

    //}

    System.out.println("----------------");
    System.out.println("size of hashmap: "+h.size());


}

}

ฉันมีหนึ่งความสับสนทำไมเราต้องแทนที่วิธีเท่ากับเมื่อเราแทนที่วิธี hashCode ในกรณีของ HashMap? ในกรณีใด ๆ hashmap จะแทนที่ค่าหาก hashcode ของวัตถุเท่ากัน
Vikas Verma

@VikasVerma hashmap ไม่ได้แทนที่ค่าใด ๆ หาก hashcode ของวัตถุเท่ากันมันจะตัดสินใจเฉพาะดัชนีที่จะต้องวางวัตถุที่เพิ่มเข้ามาใหม่ใน hashmap ขณะนี้สามารถมีวัตถุที่ดัชนีได้ดังนั้นเพื่อหลีกเลี่ยงการทำซ้ำเราจะแทนที่เมธอด equals และเราเขียนตรรกะเพื่อกำหนดว่าวัตถุทั้งสองในการเปรียบเทียบจะได้รับการปฏิบัติอย่างเท่าเทียมกันหรือไม่ ถ้าไม่ถูกแทนที่แล้วแม้ว่าวัตถุที่มีค่าเดียวกันจะถูกเก็บไว้เพราะการอ้างอิงของวัตถุทั้งสองจะแตกต่างกัน
Chetan

11

hashCode() :

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

equals() :

หากคุณแทนที่วิธีที่เท่ากันเท่านั้นa.equals(b)มันเป็นความจริงหมายความว่าhashCodea และ b ต้องเหมือนกัน แต่ไม่เกิดขึ้น เพราะคุณไม่ได้แทนที่hashCodeวิธีการ

หมายเหตุ: hashCode()เมธอดของคลาสอ็อบเจ็กต์จะส่งคืนใหม่hashCodeสำหรับแต่ละอ็อบเจ็กต์เสมอ

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


นั่นเป็นจุดที่น่าสนใจเกี่ยวกับการแทนที่ hashCode ()เท่านั้น มันโอเคเลยใช่มั้ย หรือจะมีกรณีที่มีปัญหาเช่นกัน?
จอห์นนี่

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

8

Java วางกฎไว้ว่า

"ถ้าวัตถุสองชิ้นมีค่าเท่ากันโดยใช้วิธีคลาสวัตถุเท่ากับวิธีการแฮชโค้ดควรให้ค่าเดียวกันสำหรับวัตถุทั้งสองนี้"

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


6

เพราะถ้าคุณไม่แทนที่พวกเขาคุณจะต้องใช้การฝังปริยายใน Object

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


6

ในการใช้คลาสอ็อบเจ็กต์ของเราเองเป็นกุญแจในคอลเลกชันเช่น HashMap, Hashtable ฯลฯ .. เราควรจะแทนที่ทั้งสองเมธอด (hashCode () และ equals ()) โดยการรับรู้เกี่ยวกับการทำงานภายในของคอลเลกชัน มิฉะนั้นจะนำไปสู่ผลลัพธ์ที่ไม่ถูกต้องซึ่งเราไม่คาดคิด


6

กำลังเพิ่มคำตอบของ @Lombo

เมื่อใดที่คุณจะต้องแทนที่เท่ากับ ()

การใช้งานเริ่มต้นของ Object's equals () คือ

public boolean equals(Object obj) {
        return (this == obj);
}

ซึ่งหมายความว่าสองวัตถุจะได้รับการพิจารณาที่เท่าเทียมกันหากพวกเขามีที่อยู่หน่วยความจำเดียวกันซึ่งจะเป็นจริงเฉพาะเมื่อคุณเปรียบเทียบวัตถุกับตัวเอง

แต่คุณอาจต้องการพิจารณาวัตถุสองชิ้นเหมือนกันหากพวกเขามีค่าเท่ากันสำหรับคุณสมบัติอย่างน้อยหนึ่งรายการ (ดูตัวอย่างที่ระบุในคำตอบของ @Lombo)

ดังนั้นคุณจะแทนที่equals()ในสถานการณ์เหล่านี้และคุณจะให้เงื่อนไขของคุณเองเพื่อความเท่าเทียมกัน

ฉันใช้งานได้สำเร็จเท่ากับ () และมันใช้งานได้ดีดังนั้นทำไมพวกเขาถึงขอแทนที่ hashCode () ด้วย?

ดีตราบใดที่คุณไม่ได้ใช้คอลเลกชันแบบ "Hash"กับคลาสที่ผู้ใช้กำหนดเองก็ถือว่าใช้ได้ แต่บางครั้งในอนาคตคุณอาจต้องการใช้HashMapหรือHashSetถ้าคุณไม่ใช้overrideและ"ใช้งานอย่างถูกต้อง" hashCode ()คอลเล็กชันที่ใช้แฮชเหล่านี้จะไม่ทำงานตามที่ตั้งใจไว้

แทนที่เท่านั้นเท่ากับ (เพิ่มเติมไปยัง @Lombo คำตอบของ)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

ก่อนอื่น HashMap ตรวจสอบว่า hashCode secondเป็นเหมือนกันหรือไม่firstหรือไม่ เฉพาะในกรณีที่ค่าเหมือนกันมันจะดำเนินการตรวจสอบความเท่าเทียมกันในที่ฝากข้อมูลเดียวกัน

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

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

ทำไมเราไม่สามารถตรวจสอบ HashMap เพื่อความเท่าเทียมกันในถังทั้งหมดได้? ดังนั้นฉันไม่จำเป็นต้องแทนที่ hashCode () !!

ถ้าอย่างนั้นคุณก็พลาดจุดสะสมของแฮช พิจารณาสิ่งต่อไปนี้:

Your hashCode() implementation : intObject%9.

ต่อไปนี้เป็นกุญแจที่จัดเก็บในรูปแบบของถัง

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

สมมติว่าคุณต้องการทราบว่าแผนที่มีคีย์ 10 หรือไม่คุณต้องการค้นหาที่เก็บข้อมูลทั้งหมดหรือไม่ หรือคุณต้องการที่จะค้นหาเพียงหนึ่งถัง?

จาก hashCode คุณจะระบุว่าถ้ามี 10 มันจะต้องอยู่ใน Bucket 1 ดังนั้นจะค้นหาเฉพาะ Bucket 1 เท่านั้น !!


5
class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}
  • put ('key', 'value') จะคำนวณค่าแฮชที่ใช้hashCode()เพื่อกำหนด bucket และใช้equals()เมธอดเพื่อค้นหาว่ามีค่าอยู่ใน Bucket หรือไม่ หากไม่เพิ่มก็จะถูกแทนที่ด้วยค่าปัจจุบัน
  • get ('key') จะใช้hashCode()เพื่อค้นหารายการ (bucket) ก่อนและ equals()เพื่อค้นหาค่าใน Entry

ถ้าทั้งคู่ถูกเขียนทับ

แผนที่ < >

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

ถ้าเท่ากับจะไม่ถูกแทนที่

แผนที่ < >

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

ถ้า hashCode ไม่ถูกเขียนทับ

แผนที่ < >

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

HashCode สัญญาที่เท่าเทียมกัน

  1. สองปุ่มที่เท่ากันตามวิธีที่เท่ากันควรสร้าง hashCode เดียวกัน
  2. สองคีย์ที่สร้าง hashCode เดียวกันไม่จำเป็นต้องเท่ากัน (ในตัวอย่างข้างต้นตัวเลขทั้งหมดจะสร้างรหัสแฮชเดียวกัน

4

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

สำหรับเทนนิส - เหลือง, แดง สำหรับคริกเก็ต - ขาว

ตอนนี้ถังมีลูกบอลในสามสีเหลืองแดงและขาว และตอนนี้คุณทำสีคุณเพียงรู้ว่าสีอะไรสำหรับเกม

ระบายสีลูกบอล - Hashing การเลือกลูกบอลสำหรับเกม - เท่ากับ

หากคุณทำสีและบางคนเลือกลูกบอลสำหรับคริกเก็ตหรือเทนนิสพวกเขาจะไม่รังเกียจสี !!!


4

ฉันกำลังดูคำอธิบาย "ถ้าคุณเพียงแทนที่ hashCode แล้วเมื่อคุณเรียกmyMap.put(first,someValue)มันว่าใช้ครั้งแรกให้คำนวณ hashCode และเก็บไว้ในที่ฝากข้อมูลที่กำหนดจากนั้นเมื่อคุณโทรหาmyMap.put(first,someOtherValue)มันควรแทนที่ครั้งแรกด้วยที่สองตามเอกสารแผนที่เพราะเท่ากัน (ตามคำจำกัดความของเรา) " :

ฉันคิดว่าครั้งที่ 2 เมื่อเราเพิ่มในmyMapนั้นควรเป็นวัตถุ 'สอง' เช่นmyMap.put(second,someOtherValue)


4

1) ความผิดพลาดทั่วไปแสดงในตัวอย่างด้านล่าง

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

ไม่พบรถยนต์สีเขียว

2. ปัญหาที่เกิดจาก hashCode ()

hashCode()ปัญหาที่เกิดจากวิธีการยกเลิกการแทนที่ สัญญาระหว่างequals()และhashCode()คือ:

  1. หากวัตถุสองอันเท่ากันพวกมันจะต้องมีรหัสแฮชเดียวกัน
  2. หากวัตถุสองชิ้นมีรหัสแฮชเดียวกันวัตถุเหล่านั้นอาจหรืออาจไม่เท่ากัน

    public int hashCode(){  
      return this.color.hashCode(); 
    }

4

มันจะมีประโยชน์เมื่อใช้วัตถุราคา ต่อไปนี้เป็นข้อความที่ตัดตอนมาจากที่เก็บรูปแบบพอร์ตแลนด์ :

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

ดังนั้นฉันจึงสามารถมีหลายสำเนาของวัตถุที่แสดงถึงวันที่ 16 มกราคม 1998 สำเนาใด ๆ เหล่านี้จะเท่ากัน สำหรับวัตถุขนาดเล็กเช่นนี้มันมักจะง่ายต่อการสร้างวัตถุใหม่และเคลื่อนย้ายวัตถุเหล่านั้นไปรอบ ๆ แทนที่จะใช้วัตถุเดี่ยวเพื่อแสดงวันที่

วัตถุค่าควรแทนที่. equals () ใน Java (หรือ = ใน Smalltalk) (อย่าลืมแทนที่. hashCode () ด้วย)


3

สมมติว่าคุณมี class (A) ที่รวมกันอีกสอง (B) (C) และคุณต้องเก็บอินสแตนซ์ของ (A) ไว้ใน hashtable การใช้งานเริ่มต้นอนุญาตให้แยกความแตกต่างของอินสแตนซ์เท่านั้น แต่ไม่สามารถทำได้โดย (B) และ (C) ดังนั้นอินสแตนซ์สองตัวของ A อาจเท่ากัน แต่ค่าเริ่มต้นจะไม่อนุญาตให้คุณเปรียบเทียบในวิธีที่ถูกต้อง


3

วิธีการเท่ากับและ hashcode ถูกกำหนดไว้ในระดับวัตถุ โดยค่าเริ่มต้นหากวิธีการเท่ากับผลตอบแทนจริงแล้วระบบจะไปต่อไปและตรวจสอบค่าของรหัสแฮช หากรหัสแฮชของวัตถุ 2 ตัวนั้นเหมือนกันเท่านั้นวัตถุนั้นจะได้รับการพิจารณาเหมือนกัน ดังนั้นหากคุณแทนที่เพียงเท่ากับวิธีการแม้ว่าวิธีการแทนที่เท่ากับบ่งชี้ว่า 2 วัตถุที่จะเท่ากัน hashcode ที่กำหนดโดยระบบอาจไม่บ่งชี้ว่าวัตถุ 2 มีความเท่าเทียมกัน ดังนั้นเราต้องแทนที่รหัสแฮชด้วย


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

หากมีสองวัตถุ X และ Y ซึ่งมี "วิธีการเท่ากับ" ระบุว่าพวกเขาจับคู่ แต่รหัสแฮชของ X เป็นเลขคู่และรหัสแฮชของ Y เป็นเลขคี่การรวบรวมตามที่อธิบายไว้ข้างต้นซึ่งระบุว่ารหัสแฮชของวัตถุนั้นแปลก ในรายการที่สองจะไม่สามารถหาคู่ที่ตรงกันสำหรับ Object X ได้มันจะสังเกตว่ารหัสแฮชของ X นั้นเป็นเลขคู่และเนื่องจากรายการที่สองไม่มีวัตถุใด ๆ ที่มีรหัสแฮชที่เป็นเลขคู่มันจะไม่รบกวน การค้นหามีบางสิ่งบางอย่างที่ตรงกับ X, Y แม้จะตรงกับเอ็กซ์สิ่งที่คุณควรจะพูดว่า ...
SuperCat

... อาจเป็นไปได้ว่าคอลเล็กชันจำนวนมากจะหลีกเลี่ยงการเปรียบเทียบสิ่งที่รหัสแฮชแปลว่าพวกเขาไม่สามารถเท่าเทียมกันได้ ได้รับวัตถุทั้งสองมีกัญชารหัสไม่เป็นที่รู้จักก็มักจะได้เร็วขึ้นเพื่อเปรียบเทียบพวกเขาโดยตรงมากกว่าการคำนวณรหัสแฮชของพวกเขาจึงไม่มีการรับประกันว่าสิ่งที่รายงานรหัสกัญชาไม่เท่ากัน แต่กลับไม่มีtrueสำหรับequalsจะไม่ได้รับการยกย่องในฐานะที่ตรงกัน ในทางกลับกันหากคอลเลกชันเกิดขึ้นสังเกตว่าสิ่งต่าง ๆ ไม่สามารถมีรหัสแฮชเดียวกันได้
supercat

3

วิธี Equals และ Hashcode ใน Java

พวกเขาเป็นวิธีการเรียน java.lang.Object ซึ่งเป็นชั้นซุปเปอร์ของทุกชั้นเรียน (เรียนที่กำหนดเองเช่นกันและคนอื่น ๆ ที่กำหนดไว้ใน java API)

การดำเนินงาน:

บูลีนสาธารณะเท่ากับ (Object obj)

hashCode int สาธารณะ ()

ป้อนคำอธิบายรูปภาพที่นี่

บูลีนสาธารณะเท่ากับ (Object obj)

วิธีนี้จะตรวจสอบว่าทั้งสองวัตถุอ้างอิง x และ y อ้างถึงวัตถุเดียวกัน นั่นคือตรวจสอบว่า x == y

มันสะท้อนกลับ:สำหรับค่าอ้างอิงใด ๆ x, x.equals (x) ควรกลับจริง

มันเป็นสมมาตร:สำหรับค่าอ้างอิงใด ๆ x และ y, x.equals (y) ควรกลับจริงถ้าหาก y.equals (x) คืนค่าจริง

มันคือสกรรมกริยา:สำหรับค่าอ้างอิงใด ๆ x, y และ z หาก x.equals (y) ส่งคืนค่าจริงและ y.equals (z) ส่งคืนค่าจริงแล้ว x.equals (z) ควรกลับจริง

มันสอดคล้องกัน:สำหรับค่าอ้างอิงใด ๆ ที่ x และ y การเรียกใช้หลายครั้งของ x.equals (y) จะคืนค่าจริงหรือคืนเท็จอย่างสม่ำเสมอหากไม่มีข้อมูลที่ใช้ในการเปรียบเทียบเท่ากับการแก้ไขวัตถุ

สำหรับค่าอ้างอิงใด ๆ ที่ไม่ใช่ค่า null x, x.equals (null) ควรส่งคืนค่าเท็จ

hashCode int สาธารณะ ()

วิธีนี้จะส่งกลับค่ารหัสแฮชสำหรับวัตถุที่มีการเรียกใช้วิธีนี้ เมธอดนี้ส่งคืนค่ารหัสแฮชเป็นจำนวนเต็มและได้รับการสนับสนุนเพื่อประโยชน์ของคลาสคอลเลกชันที่ใช้การแฮชเช่น Hashtable, HashMap, HashSet เป็นต้นวิธีนี้จะต้องถูกแทนที่ในทุก ๆ คลาสที่แทนที่เมธอดเท่ากับ

สัญญาทั่วไปของ hashCode คือ:

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

จำนวนเต็มนี้ไม่จำเป็นต้องคงที่จากการเรียกใช้แอปพลิเคชันหนึ่งไปยังการเรียกใช้แอปพลิเคชันเดียวกันอีกครั้ง

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

ไม่จำเป็นว่าถ้าวัตถุสองชิ้นไม่เท่ากันตามวิธี equals (java.lang.Object) ดังนั้นการเรียกเมธอด hashCode ในแต่ละวัตถุทั้งสองนั้นจะต้องให้ผลลัพธ์ที่เป็นจำนวนเต็มชัดเจน อย่างไรก็ตามโปรแกรมเมอร์ควรตระหนักว่าการสร้างผลลัพธ์จำนวนเต็มที่แตกต่างกันสำหรับวัตถุที่ไม่เท่ากันอาจปรับปรุงประสิทธิภาพของแฮชเทเบิลได้

วัตถุที่เท่าเทียมกันจะต้องสร้างรหัสแฮชเดียวกันตราบเท่าที่พวกเขาเท่ากัน แต่วัตถุที่ไม่เท่ากันนั้นไม่จำเป็นต้องสร้างรหัสแฮชที่แตกต่างกัน

แหล่งข้อมูล:

JavaRanch

ภาพ


รูปภาพ (ลิงค์วิดีโอ) อยู่ในโหมดส่วนตัว ทำให้เป็นสาธารณะเพื่อดู
UdayKiran Pulipati

2

ในตัวอย่างด้านล่างหากคุณแสดงความคิดเห็นการแทนที่สำหรับเท่ากับหรือแฮชโค้ดในคลาสบุคคลรหัสนี้จะล้มเหลวในการค้นหาคำสั่งซื้อของทอม การใช้การใช้งานค่าเริ่มต้นของ hashcode อาจทำให้เกิดความล้มเหลวในการค้นหา hashtable

สิ่งที่ฉันมีด้านล่างคือรหัสแบบง่ายที่ดึงลำดับของผู้คนโดยบุคคล บุคคลกำลังถูกใช้เป็นกุญแจสำคัญใน hashtable

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

    public Person(String name, int age, String socialSecurityNumber) {
        this.name = name;
        this.age = age;
        this.socialSecurityNumber = socialSecurityNumber;
    }

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}

2

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

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

คุณต้องแทนที่ hashCode () วิธีการพร้อมกับ equals () เพราะ equals () ทำงานตาม hashcode

นอกจากนี้การเอาชนะ hashCode () วิธีการพร้อมกับ equals () ช่วยในการทำสัญญาเท่ากับ () - สัญญา hashCode (): "ถ้าวัตถุสองอันมีค่าเท่ากันพวกเขาจะต้องมีรหัสแฮชเดียวกัน"

เมื่อใดที่คุณต้องเขียนการใช้งานที่กำหนดเองสำหรับ hashCode ()

ดังที่เราทราบว่าการทำงานภายในของ HashMap นั้นเป็นไปตามหลักการของ Hashing มีที่เก็บข้อมูลบางอย่างที่จัดเก็บรายการไว้ คุณปรับแต่งการใช้ hashCode () ตามความต้องการของคุณเพื่อให้วัตถุประเภทเดียวกันสามารถเก็บไว้ในดัชนีเดียวกัน เมื่อคุณเก็บค่าลงใน Map collection โดยใช้put(k,v)เมธอดการใช้งานภายในของ put () คือ:

put(k, v){
hash(k);
index=hash & (n-1);
}

หมายความว่ามันสร้างดัชนีและดัชนีถูกสร้างขึ้นตาม hashcode ของวัตถุสำคัญโดยเฉพาะ ดังนั้นให้วิธีนี้สร้าง hashcode ตามความต้องการของคุณเพราะรายการ hashcode เดียวกันจะถูกเก็บไว้ในที่ฝากข้อมูลหรือดัชนีเดียวกัน

แค่นั้นแหละ!


1

hashCode()วิธีการที่ใช้ในการรับจำนวนเต็มเฉพาะสำหรับวัตถุที่กำหนด จำนวนเต็มนี้จะใช้สำหรับการกำหนดสถานที่ตั้งถังเมื่อวัตถุนี้จะต้องเก็บไว้ในบางHashTable, HashMapเช่นโครงสร้างข้อมูล ตามค่าเริ่มต้นhashCode()เมธอดของ Object จะคืนค่าและแสดงจำนวนหน่วยความจำที่อยู่ของที่เก็บวัตถุ

hashCode()วิธีการของวัตถุที่จะใช้เมื่อเราใส่ไว้ในHashTable, หรือHashMap HashSetเพิ่มเติมเกี่ยวHashTablesกับ Wikipedia.org สำหรับการอ้างอิง

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

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

ดังนั้นในขณะที่เราสามารถมองเห็นการรวมกันของที่hashCode()และวิธีการจะใช้เมื่อการจัดเก็บและเมื่อมองวัตถุในequals()HashTable

หมายเหตุ:

  1. ใช้คุณลักษณะเดียวกันของวัตถุเพื่อสร้างhashCode()และequals()ทั้งสอง ดังเช่นในกรณีของเราเราใช้รหัสพนักงาน

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

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

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

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html


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

@EJP ส่วนใหญ่ hascode () จะส่งคืน interger ที่ไม่ซ้ำกันสำหรับสองวัตถุที่แตกต่างกัน แต่มีจะเป็นโอกาสในการชนกัน hascode สองวัตถุที่แตกต่างกันแนวคิดนี้เรียกว่าเป็นแฮชโค้ดชน โปรดอ้างอิง: tech.queryhome.com/96931/…
Paramesh Korrakuti

1

IMHO มันเป็นไปตามกฎบอกว่า - หากวัตถุสองอันเท่ากันพวกมันควรจะมีแฮชเดียวกันนั่นคือวัตถุที่เท่ากันควรสร้างค่าแฮชเท่ากัน

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

หากคุณต้องการใช้วัตถุที่กำหนดเองในคอลเลกชันที่ใช้ Hash คุณจะต้องแทนที่ทั้ง equals () และ hashCode () ตัวอย่างเช่นหากฉันต้องการรักษา HashSet ของ Employee Objects ถ้าฉันไม่ใช้ hashCode ที่แข็งแกร่งและเท่ากับ ฉันอาจจบการทำงานกับ Employee Objects สองแบบที่แตกต่างกันเกิดขึ้นเมื่อฉันใช้อายุเป็น hashCode () แต่ฉันควรใช้ค่าที่ไม่ซ้ำซึ่งสามารถเป็นรหัสพนักงานได้


1

เพื่อช่วยคุณตรวจสอบวัตถุที่ซ้ำกันเราจำเป็นต้องมีค่าเท่ากับและ hashCode

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


1

เมื่อคุณต้องการที่จะจัดเก็บและดึงวัตถุที่กำหนดเองของคุณเป็นกุญแจสำคัญในแผนที่จากนั้นคุณควรแทนที่เท่ากับและ hashCode ในวัตถุที่กำหนดเองของคุณ เช่น:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

ที่นี่ p1 & p2 จะพิจารณาว่าเป็นเพียงหนึ่งวัตถุและmapขนาดจะเท่ากับ 1 เพียงเพราะมันเท่ากัน


1
public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

ชั้นทดสอบ

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

ใน Object Class equals (Object obj) จะใช้ในการเปรียบเทียบการเปรียบเทียบที่อยู่นั่นคือเหตุผลที่เมื่อในการทดสอบคลาสถ้าคุณเปรียบเทียบสองวัตถุแล้วเท่ากับวิธีการให้เท็จ แต่เมื่อเราแทนที่ hashcode () มันสามารถเปรียบเทียบเนื้อหาและให้ผลลัพธ์ที่เหมาะสม


และชั้นเรียนทดสอบที่ฉันเพิ่มในโปรแกรมด้านล่าง
Manash Ranjan Dakua

ใน Object Class equals (Object obj) ถูกใช้เพื่อเปรียบเทียบการเปรียบเทียบที่อยู่นั่นคือเหตุผลว่าทำไมเมื่อใน Test Class ถ้าคุณเปรียบเทียบสองวัตถุแล้วเท่ากับวิธีการให้เท็จ แต่เมื่อเราแทนที่ hashcode () มันสามารถเปรียบเทียบเนื้อหาและให้ผลลัพธ์ที่เหมาะสม
Manash Ranjan Dakua

1
คุณสามารถใช้ลิงก์แก้ไขเพียงด้านล่างคำตอบนี้จะเพิ่มให้กับคำตอบของคุณ .. โปรดอย่าเพิ่มคำตอบเป็นสองคนที่ไม่สมบูรณ์
Suraj ราว

1

หากคุณลบล้างequals()และไม่ทำhashcode()เช่นนั้นคุณจะไม่พบปัญหาใด ๆ ยกเว้นว่าคุณหรือคนอื่นใช้ประเภทคลาสนั้นในคอลเล็กชันที่แฮชเช่นHashSetเว้นแต่ว่าคุณหรือคนอื่นใช้ว่าประเภทชั้นในคอลเลกชันแฮชเช่น ผู้คนก่อนหน้าฉันอธิบายทฤษฎีเอกสารอย่างชัดเจนหลายครั้งฉันแค่มาที่นี่เพื่อเป็นตัวอย่างที่ง่ายมาก

พิจารณาชั้นเรียนที่equals()ต้องหมายถึงบางสิ่งที่ปรับแต่งเอง: -

    public class Rishav {

        private String rshv;

        public Rishav(String rshv) {
            this.rshv = rshv;
        }

        /**
        * @return the rshv
        */
        public String getRshv() {
            return rshv;
        }

        /**
        * @param rshv the rshv to set
        */
        public void setRshv(String rshv) {
            this.rshv = rshv;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Rishav) {
                obj = (Rishav) obj;
                if (this.rshv.equals(((Rishav) obj).getRshv())) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return rshv.hashCode();
        }

    }

พิจารณาคลาสหลักนี้: -

    import java.util.HashSet;
    import java.util.Set;

    public class TestRishav {

        public static void main(String[] args) {
            Rishav rA = new Rishav("rishav");
            Rishav rB = new Rishav("rishav");
            System.out.println(rA.equals(rB));
            System.out.println("-----------------------------------");

            Set<Rishav> hashed = new HashSet<>();
            hashed.add(rA);
            System.out.println(hashed.contains(rB));
            System.out.println("-----------------------------------");

            hashed.add(rB);
            System.out.println(hashed.size());
        }

    }

สิ่งนี้จะให้ผลลัพธ์ต่อไปนี้: -

    true
    -----------------------------------
    true
    -----------------------------------
    1

ฉันมีความสุขกับผลลัพธ์ แต่ถ้าฉันไม่ได้ลบล้างhashCode()มันจะทำให้ฝันร้ายเป็นวัตถุที่Rishavมีเนื้อหาสมาชิกเดียวกันจะไม่ได้รับการปฏิบัติที่ไม่เหมือนที่hashCodeจะแตกต่างกันตามที่สร้างโดยพฤติกรรมเริ่มต้นนี่คือผลลัพธ์: -

    true
    -----------------------------------
    false
    -----------------------------------
    2

0

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

สำหรับ Ex: equals () วิธีการในวัตถุเพียงตรวจสอบความเท่าเทียมกันในการอ้างอิง ดังนั้นหากคุณต้องการเปรียบเทียบสถานะของมันเช่นกันคุณสามารถลบล้างสิ่งนั้นได้ในคลาส String


-3

Bah - "คุณต้องลบล้าง hashCode () ในทุก ๆ คลาสที่แทนที่ ()"

[จากจาวาที่มีประสิทธิภาพโดย Joshua Bloch?]

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

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.