เหตุใดจึงแทนที่วิธีเท่ากับและ hashcode ใน Java
ที่มา:
Medium บทความนี้เน้นที่วิธีที่เกี่ยวข้องกันสองวิธี: เท่ากับ()และhashcode() คุณจะได้เรียนรู้วิธีที่พวกเขาโต้ตอบกัน และวิธีแทนที่พวกเขาอย่างถูกต้อง
เหตุใดเราจึงแทนที่วิธีเท่ากับ ()
ใน Java เราไม่สามารถโอเวอร์โหลดพฤติกรรมของตัวดำเนินการเช่น
== ,
+= ,
- + พวกเขาทำงานตามกระบวนการที่กำหนด ตัวอย่างเช่น พิจารณาการทำงานของ ตัวดำเนิน การ
==
ตัวดำเนินการ == ทำงานอย่างไร
จะตรวจสอบว่าการอ้างอิงทั้งสองที่ถูกเปรียบเทียบชี้ไปที่อินสแตนซ์เดียวกันในหน่วยความจำหรือไม่ ตัวดำเนินการ
==จะประเมินเป็นจริงก็ต่อเมื่อการอ้างอิงทั้งสองแสดงถึงอินสแตนซ์เดียวกันในหน่วยความจำ ลองมาดูโค้ดตัวอย่าง:
public class Person {
private Integer age;
private String name;
..getters, setters, constructors
}
สมมติว่าในโปรแกรมของคุณคุณได้สร้าง วัตถุ
บุคคล สองตัว ในตำแหน่งที่แตกต่างกันและต้องการเปรียบเทียบวัตถุเหล่านั้น
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
จากมุมมองทางธุรกิจทั้งสองมีลักษณะเหมือนกันใช่ไหม? แต่สำหรับ JVM พวกเขาไม่เหมือนกัน เนื่องจากทั้งคู่ถูกสร้างขึ้นโดยใช้ คีย์เวิร์ด
ใหม่ อินสแตน ซ์เหล่านี้จึงอยู่ในเซ็กเมนต์หน่วยความจำที่แตกต่างกัน ดังนั้น ตัวดำเนินการ
==จะคืนค่า
false แต่ถ้าเราไม่สามารถแทนที่ ตัวดำเนินการ
== ได้แล้วเราจะบอก JVM ได้อย่างไรว่าเราต้องการให้วัตถุทั้งสองนี้ได้รับการปฏิบัติเหมือนกัน นี่คือที่ มาของวิธีการ.
equals() คุณสามารถแทนที่
เท่ากับ()เพื่อตรวจสอบว่าวัตถุบางตัวมีค่าเหมือนกันสำหรับบางฟิลด์เพื่อพิจารณาว่าเท่ากันหรือไม่ คุณสามารถเลือกเขตข้อมูลที่จะเปรียบเทียบได้ หากเราบอกว่า วัตถุ
บุคคล สองคน จะเหมือนกันก็ต่อเมื่อพวกเขามีอายุเท่ากันและมีชื่อเหมือนกัน IDE จะสร้างสิ่ง นี้เพื่อสร้างโดยอัตโนมัติ
เท่ากับ() :
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
กลับไปที่ตัวอย่างก่อนหน้านี้ของเรา
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
ใช่ เราไม่สามารถโอเวอร์โหลด ตัวดำเนินการ
==เพื่อเปรียบเทียบวัตถุในแบบที่เราต้องการได้ แต่ Java ให้วิธีอื่นแก่เรา - วิธี
เท่ากับ ()ซึ่งเราสามารถแทนที่ได้ตามที่เราต้องการ
โปรดทราบว่าหากเราไม่ได้จัดเตรียม.equals() เวอร์ชันที่กำหนดเอง (หรือที่เรียกว่าการแทนที่) ในคลาสของเรา ดังนั้น . equals() ที่กำหนดไว้ล่วงหน้า จาก คลาส Objectและ ตัวดำเนินการ ==จะทำงานเหมือนกัน วิธีการ เท่ากับ () เริ่มต้นซึ่งสืบทอดมาจาก
Objectจะตรวจสอบว่าทั้งสองอินสแตนซ์ที่ถูกเปรียบเทียบนั้นเหมือนกันในหน่วยความจำหรือไม่!
เหตุใดเราจึงเอาชนะวิธี hashCode()
โครงสร้างข้อมูลบางอย่างใน Java เช่น
HashSetและ
HashMapจัดเก็บองค์ประกอบตามฟังก์ชันแฮชที่ใช้กับองค์ประกอบเหล่านั้น ฟังก์ชันแฮชคือ
hashCode( ) หากเรามีตัวเลือกในการเอาชนะ
เมธอด .equals()เราก็ควรมีตัวเลือกในการเอาชนะ
เมธอด hashCode() ด้วย มีเหตุผลสำหรับเรื่องนี้ ท้ายที่สุด การใช้งานเริ่มต้น
ของ hashCode()ซึ่งสืบทอดมาจาก
Objectจะถือว่าวัตถุทั้งหมดในหน่วยความจำไม่ซ้ำกัน! แต่กลับไปที่โครงสร้างข้อมูลแฮชเหล่านี้กันดีกว่า มีกฎสำหรับโครงสร้างข้อมูลเหล่านี้
HashSetไม่สามารถมีค่าที่ซ้ำกันและHashMapไม่สามารถมีคีย์ที่ซ้ำกัน HashSetถูกนำไปใช้โดยใช้
HashMapในลักษณะที่ ค่า
HashSet แต่ละค่าจะ ถูกจัดเก็บเป็นคีย์ใน
HashMap HashMapทำงานอย่างไร
HashMapเป็นอาร์เรย์ดั้งเดิมที่มีหลายส่วน แต่ละส่วนมีรายการที่เชื่อมโยง (
linkedList ) รายการที่เชื่อมโยงนี้เก็บกุญแจของเรา
HashMapค้นหา linkedList ที่ถูกต้องสำหรับแต่ละคีย์โดยใช้ เมธอด
hashCode()จากนั้นวนซ้ำองค์ประกอบทั้งหมดของ
linkedList นั้น และใช้ เมธอด
เท่ากับ ()กับแต่ละองค์ประกอบเหล่านั้นเพื่อตรวจสอบว่าองค์ประกอบนั้นมีอยู่ที่นั่นหรือไม่
ไม่อนุญาตให้ใช้คีย์ซ้ำกัน เมื่อเราใส่บางสิ่งไว้ใน
HashMapคีย์จะถูกจัดเก็บไว้ในรายการที่เชื่อมโยงรายการใดรายการหนึ่งเหล่านี้ รายการลิงก์ใดที่คีย์นี้จะถูกจัดเก็บจะแสดงโดยผลลัพธ์ของ เมธอด
hashCode()สำหรับคีย์นั้น นั่นคือหาก
key1.hashCode()ผลลัพธ์เป็น 4 ดังนั้น
key1 นั้น จะถูกจัดเก็บไว้ในส่วนที่ 4 ของอาร์เรย์ใน
LinkedList ที่มีอยู่อยู่ที่ นั่น ตามค่าเริ่มต้น เมธอด
hashCode()จะส่งกลับผลลัพธ์ที่แตกต่างกันสำหรับแต่ละอินสแตนซ์ หากเรามีค่าเริ่มต้น
เท่ากับ()ที่ทำงานเหมือน
==โดยถือว่าอินสแตนซ์ทั้งหมดในหน่วยความจำเป็นวัตถุที่แตกต่างกัน ก็จะไม่มีปัญหา ดังที่คุณอาจจำได้ ในตัวอย่างก่อนหน้านี้ เราได้กล่าวว่าเราต้องการให้ อินสแตนซ์
Personได้รับการพิจารณาเท่ากัน หากอายุและชื่อเท่ากัน
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1.equals(person2) ); --> will print true!
ตอนนี้เรามาสร้างแผนที่เพื่อจัดเก็บอินสแตนซ์เหล่านี้เป็นคีย์โดยมีสตริงเฉพาะเป็นคู่ของค่า
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
ในคลาส
Personเราไม่ได้แทนที่เมธอด
hashCodeแต่เรามีเมธอด
เท่ากับที่ ถูกแทนที่ เนื่องจาก
hashCode เริ่มต้น ให้ผลลัพธ์ที่แตกต่างกันสำหรับอินสแตนซ์ Java ที่แตกต่างกัน
ของ person1.hashCode()และ
person2.hashCode()จึงมีโอกาสสูงที่จะได้รับผลลัพธ์ที่แตกต่างกัน แผนที่ของเราสามารถลงท้ายด้วย
บุคคล ต่างๆ ในรายการลิงก์ที่แตกต่างกัน สิ่ง นี้
ขัดแย้งกับตรรกะ
ของ HashMap ท้ายที่สุดHashMapไม่สามารถมีคีย์ที่เหมือนกันหลายคีย์ได้! ประเด็นก็คือค่าเริ่มต้น
hashCode()ที่สืบทอดมาจาก คลาส
Objectนั้นไม่เพียงพอ แม้ว่าเราจะแทนที่เมธอด
เท่ากับ () ของ คลาส
Personแล้ว นั่นเป็นสาเหตุที่เราต้องแทนที่เมธอด
hashCode()หลังจากที่เราแทนที่ เมธอด
เท่ากับ ตอนนี้เรามาแก้ไขปัญหานี้กัน เราจำเป็นต้องแทนที่เมธอด
hashCode() ของเรา เพื่อที่จะคำนึงถึงฟิลด์เดียวกันกับ เท่ากับ (
)นั่นคือ
อายุและ
ชื่อ
public class Person {
private Integer age;
private String name;
..getters, setters, constructors
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
@Override
public int hashCode() {
int prime = 31;
return prime*Objects.hash(name, age);
}
ใน เมธอด
hashCode()เราใช้ค่าธรรมดา (คุณสามารถใช้ค่าอื่นก็ได้) แต่แนะนำให้ใช้จำนวนเฉพาะเพื่อสร้างปัญหาให้น้อยลง
ลองเก็บคีย์เหล่านี้ไว้ใน HashMap ของเรา อีกครั้ง:
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode()และ
person2.hashCode()จะเหมือนกัน สมมติว่าเป็น 0
HashMapจะไปที่เซ็กเมนต์ 0 และในนั้น
LinkedListจะบันทึก
person1เป็นคีย์ที่มีค่า “1” ในกรณีที่สอง เมื่อ
HashMapไปที่บัคเก็ต 0 อีกครั้งเพื่อจัดเก็บคีย์
person2ที่มีค่า "2" ก็จะเห็นว่ามีคีย์อื่นที่เท่ากับมีอยู่แล้วที่นั่น วิธีนี้จะเขียนทับคีย์ก่อนหน้า และ มีเพียง
บุคคล สำคัญ 2 เท่านั้น ที่จะอยู่ ใน
HashMap ของเรา นี่คือวิธีที่เราเรียนรู้วิธี การทำงานของกฎ
HashMapซึ่งระบุว่าคุณไม่สามารถใช้คีย์ที่เหมือนกันหลายคีย์ได้!
อย่างไรก็ตาม โปรดทราบว่าอินสแตนซ์ที่ไม่เท่ากันสามารถมีแฮชโค้ดเดียวกันได้ และอินสแตนซ์ที่เท่ากันจะต้องส่งคืนแฮชโค้ดเดียวกัน
GO TO FULL VERSION