การเปรียบเทียบวัตถุ Java ด้วยเท่ากับ () และ hashcode ()

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

หากไม่มีequals()และhashcode()เราจะต้องสร้างifการเปรียบเทียบ " " ที่มีขนาดใหญ่มากโดยเปรียบเทียบทุกฟิลด์จากวัตถุ สิ่งนี้จะทำให้โค้ดสับสนและอ่านยากมาก วิธีการทั้งสองร่วมกันนี้ช่วยให้เราสร้างรหัสที่ยืดหยุ่นและเหนียวแน่นมากขึ้น

รับซอร์สโค้ด Java Challengers

การแทนที่ equals () และ hashcode () ใน Java

วิธีการแทนที่เป็นเทคนิคที่พฤติกรรมของคลาสพาเรนต์หรืออินเตอร์เฟสถูกเขียนอีกครั้ง (แทนที่) ในคลาสย่อยเพื่อใช้ประโยชน์จากโพลีมอร์ฟิซึม ทุกตัวObjectใน Java มี an equals()และhashcode()method แต่ต้องถูกแทนที่เพื่อให้ทำงานได้อย่างถูกต้อง

เพื่อทำความเข้าใจว่าการลบล้างทำงานอย่างไรequals()และ   hashcode()เราสามารถศึกษาการนำไปใช้งานได้ในคลาส Java หลัก ด้านล่างนี้คือequals()วิธีการในObjectคลาส วิธีนี้กำลังตรวจสอบว่าอินสแตนซ์ปัจจุบันเหมือนกับที่ผ่านไปก่อนหน้านี้Objectหรือไม่

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

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

 @HotSpotIntrinsicCandidate public native int hashCode(); 

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

ตามกฎแล้วเมื่อคุณลบล้างequals()คุณจะต้องลบล้างhashcode()ด้วย

การเปรียบเทียบวัตถุด้วยเท่ากับ ()

เราใช้equals()วิธีเปรียบเทียบวัตถุใน Java ในการตรวจสอบว่าวัตถุสองชิ้นเหมือนกันหรือไม่ให้equals()เปรียบเทียบค่าของแอตทริบิวต์ของวัตถุ:

 public class EqualsAndHashCodeExample { public static void main(String... equalsExplanation) { System.out.println(new Simpson("Homer", 35, 120) .equals(new Simpson("Homer",35,120))); System.out.println(new Simpson("Bart", 10, 120) .equals(new Simpson("El Barto", 10, 45))); System.out.println(new Simpson("Lisa", 54, 60) .equals(new Object())); } static class Simpson { private String name; private int age; private int weight; public Simpson(String name, int age, int weight) { this.name = name; this.age = age; this.weight = weight; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Simpson simpson = (Simpson) o; return age == simpson.age && weight == simpson.weight && name.equals(simpson.name); } } } 

ในการเปรียบเทียบครั้งแรกให้equals()เปรียบเทียบอินสแตนซ์วัตถุปัจจุบันกับวัตถุที่ถูกส่งผ่าน หากวัตถุทั้งสองมีค่าเท่ากันequals()จะคืนtrueค่า

ในการเปรียบเทียบครั้งที่สองequals()ตรวจสอบว่าวัตถุที่ส่งผ่านนั้นเป็นโมฆะหรือไม่หรือพิมพ์เป็นคลาสอื่น ถ้าเป็นคลาสอื่นวัตถุจะไม่เท่ากัน

สุดท้ายequals()เปรียบเทียบฟิลด์ของวัตถุ ถ้าวัตถุสองชิ้นมีค่าฟิลด์เดียวกันแสดงว่าวัตถุนั้นเหมือนกัน

การวิเคราะห์การเปรียบเทียบวัตถุ

ตอนนี้เรามาดูผลลัพธ์ของการเปรียบเทียบเหล่านี้ในmain()วิธีการของเรา อันดับแรกเราเปรียบเทียบสองSimpsonวัตถุ:

 System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120))); 

trueวัตถุที่นี่เหมือนกันดังนั้นผลจะเป็น

ต่อไปเราจะเปรียบเทียบสองSimpsonวัตถุอีกครั้ง:

 System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45))); 

วัตถุที่นี่เกือบจะเหมือนกัน แต่ชื่อต่างกัน: Bart และ El Barto ดังนั้นผลลัพธ์จะเป็นfalseอย่างไร

สุดท้ายลองเปรียบเทียบSimpsonวัตถุกับตัวอย่างของคลาส Object:

 System.out.println(new Simpson("Lisa", 54, 60).equals(new Object())); 

ในกรณีนี้ผลลัพธ์จะเป็นfalseเพราะประเภทคลาสแตกต่างกัน

เท่ากับ () เทียบกับ ==

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

 System.out.println(homer == homer2); 

ในการเปรียบเทียบครั้งแรกเราสร้างอินสแตนซ์สองSimpsonอินสแตนซ์ที่แตกต่างกันโดยใช้ตัวnewดำเนินการ ด้วยเหตุนี้ตัวแปรhomerและhomer2จะชี้ไปยังObjectการอ้างอิงที่แตกต่างกันในฮีปหน่วยความจำ เราจะได้falseผลลัพธ์

System.out.println(homer.equals(homer2)); 

ในการเปรียบเทียบครั้งที่สองเราจะแทนที่equals()เมธอด ในกรณีนี้จะมีการเปรียบเทียบชื่อเท่านั้น เนื่องจากชื่อของSimpsonวัตถุทั้งสองคือ“ โฮเมอร์” ผลลัพธ์จะเป็นtrueอย่างไร

การระบุวัตถุที่ไม่ซ้ำกันด้วย hashcode ()

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

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

นี่คือตัวอย่างที่ใช้ได้จริงกับhashcode().

 public class HashcodeConcept { public static void main(String... hashcodeExample) { Simpson homer = new Simpson(1, "Homer"); Simpson bart = new Simpson(2, "Homer"); boolean isHashcodeEquals = homer.hashCode() == bart.hashCode(); if (isHashcodeEquals) { System.out.println("Should compare with equals method too."); } else { System.out.println("Should not compare with equals method because " + "the id is different, that means the objects are not equals for sure."); } } static class Simpson { int id; String name; public Simpson(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o)  if (this == o) return true; if (o == null  @Override public int hashCode() { return id; } } } 

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

ใช้ equals () และ hashcode () กับคอลเลกชัน

The Set interface is responsible for ensuring no duplicate elements will be inserted in a Set subclass. The following are some of the classes that implement the Set interface:

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

Only unique elements may be inserted into a Set, so if you want to add an element to the HashSet class (for example), you must first use the equals() and hashcode() methods to verify that the element is unique. If the equals() and hashcode()methods weren’t overridden in this case, you would risk inserting duplicate elements in the code.

In the code below, we’re using the add method to add a new element  to a HashSet object. Before the new element is added, HashSet checks to see whether the element  already exists in the given collection:

 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; 

If the object is the same, the new element won’t be inserted.

Hash collections

Set isn’t the only collection that makes use of equals() and hashcode(). HashMap, Hashtable, and LinkedHashMap also require these methods. As a rule, if you see a collection that has the prefix of “Hash,” you can be sure that it requires overriding the hashcode() and equals() methods to make their features work properly.  

Guidelines for using equals() and hashcode()

You should only execute an equals() method for objects that have the same unique hashcode ID. You should not execute equals() when the hashcode ID is different.

Table 1. Hashcode comparisons

If the hashcode() comparison ... Then …
returns true execute equals()
returns false do not execute equals()

This principle is mainly used in Set or Hash collections for performance reasons.

Rules for object comparison

When a hashcode() comparison returns false, the equals() method must also return false. If the hashcode is different, then the objects are definitely not equal.

Table 2. Object comparison with hashcode()

When the hashcode comparison returns ... The equals() method should return ...
true true or false
false false

When the equals() method returns true, it means that the objects are equal in all values and attributes. In this case,  the hashcode comparison must be true as well.

Table 3. Object comparison with equals()

When the equals() method returns ... The hashcode() method should return ...
true true
false true or false

Take the equals() and hashcode() challenge!

It’s time to test your skills with the equals() and hashcode() methods.  Your goal in this challenge is to figure out the output of the two equals() method comparisons and guess the size of the Set collection.

To start, study the following code carefully:

 public class EqualsHashCodeChallenge { public static void main(String... doYourBest) { System.out.println(new Simpson("Bart").equals(new Simpson("Bart"))); Simpson overriddenHomer = new Simpson("Homer") { public int hashCode() { return (43 + 777) + 1; } }; System.out.println(new Simpson("Homer").equals(overriddenHomer)); Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge"))); set.add(new Simpson("Homer")); set.add(overriddenHomer); System.out.println(set.size()); } static class Simpson { String name; Simpson(String name) { this.name = name; } @Override public boolean equals(Object obj) { Simpson otherSimpson = (Simpson) obj; return this.name.equals(otherSimpson.name) && this.hashCode() == otherSimpson.hashCode(); } @Override public int hashCode() { return (43 + 777); } } } 

Remember, analyze the code first, guess the result, and then run the code. Your goal is to improve your skill with code analysis and absorb core Java concepts to make your code more powerful. Choose your answer before checking the correct answer below.

 A) true true 4 B) true false 3 C) true false 2 D) false true 3 

What just happened? Understanding equals() and hashcode()

In the first equals() method comparison, the result is true because the state of the object is exactly the same and the hashcode() method returns the same value for both objects.

In the second equals() method comparison, the hashcode() method is being overridden for the overridenHomer variable. The name is “Homer” for both Simpson objects, but the hashcode() method returns a different value for overriddenHomer. In this case, the final result from the the equals() method will be false because the method contains a comparison with the hashcode.

You might notice that the size of the collection is set to hold three Simpson objects. Let’s check this in a detailed way.

The first object in the set will be will be inserted normally:

 new Simpson("Homer"); 

The next object will be inserted normally, as well, because it holds a different value from the previous object:

 new Simpson("Marge"); 

Finally,  the following Simpson object has the same value as the first object. In this case the object won’t be inserted:

 set.add(new Simpson("Homer")); 

ดังที่เราทราบoverridenHomerออบเจ็กต์ใช้ค่าแฮชโค้ดที่แตกต่างจากการSimpson(“Homer”)สร้างอินสแตนซ์ปกติ ด้วยเหตุนี้องค์ประกอบนี้จะถูกแทรกลงในคอลเล็กชัน:

 overriddenHomer; 

คีย์คำตอบ

คำตอบนี้ผู้ท้าชิง Java เป็นB ผลลัพธ์จะเป็น:

 true false 3 

วิดีโอท้า! การดีบักเท่ากับ () และ hashcode ()

การดีบักเป็นวิธีที่ง่ายที่สุดวิธีหนึ่งในการดูดซับแนวคิดการเขียนโปรแกรมอย่างเต็มที่ในขณะเดียวกันก็ปรับปรุงโค้ดของคุณด้วย ในวิดีโอนี้คุณสามารถทำตามในขณะที่ฉันแก้ปัญหาและอธิบาย Java equals()และhashcode()ความท้าทาย