1. การเปรียบเทียบวัตถุใน Java
ใน Java วัตถุสามารถเปรียบเทียบได้ทั้งโดยการอ้างอิงและตามค่า
การเปรียบเทียบการอ้างอิง
หากตัวแปรสองตัวชี้ไปที่วัตถุเดียวกันในหน่วยความจำ การอ้างอิงที่จัดเก็บไว้ในตัวแปรเหล่านี้จะมีค่าเท่ากัน หากคุณเปรียบเทียบตัวแปรเหล่านี้โดยใช้ตัวดำเนินการความเท่าเทียมกัน ( ==
) คุณจะพบว่าเป็นจริง และผลลัพธ์นั้นสมเหตุสมผล ทุกอย่างเรียบง่ายที่นี่
รหัส | เอาต์พุตคอนโซล |
---|---|
|
|
เปรียบเทียบตามมูลค่า
แต่คุณมักจะพบกับสถานการณ์ที่ตัวแปรสองตัวอ้างถึงออบเจกต์สองตัวที่เหมือนกัน ตัวอย่างเช่น วัตถุสตริงที่แตกต่างกันสองรายการที่มีข้อความเดียวกัน
ในการตรวจสอบว่าวัตถุต่างๆ เหมือนกันหรือไม่ ให้ใช้equals()
วิธีการ ตัวอย่างเช่น:
รหัส | เอาต์พุตคอนโซล |
---|---|
|
|
เมธอดequals
ไม่จำกัดString
คลาส ทุกชนชั้นเลยก็ว่าได้
แม้แต่ชั้นเรียนที่คุณเขียนขึ้นเอง และนี่คือเหตุผล
2. Object
ชั้นเรียน
คลาสทั้งหมดใน Java สืบทอดObject
คลาส ผู้สร้างของ Java มาพร้อมกับแนวทางนี้
และถ้าคลาสสืบทอดObject
คลาส มันก็จะได้รับเมธอดทั้งหมดของObject
คลาส นั้น และนี่คือผลสืบเนื่องที่สำคัญของมรดก
กล่าวอีกนัยหนึ่ง ทุกคลาสมีเมธอดของObject
คลาส แม้ว่าโค้ดจะไม่ได้กล่าวถึงก็ตาม
วิธีการสืบทอดเหล่านี้รวมถึงวิธีการที่เกี่ยวข้องกับการเปรียบเทียบวัตถุ เหล่านี้คือequals()
วิธีhashCode()
การ
รหัส | ในความเป็นจริง นี่คือสิ่งที่เราจะได้: |
---|---|
|
|
ในตัวอย่างข้างต้น เราสร้างPerson
คลาสอย่างง่ายด้วยพารามิเตอร์ชื่อและอายุ แต่ไม่ใช่เมธอดเดียว แต่เนื่องจากทุกคลาสสืบทอดObject
คลาส มา คลาส Person
จึงมีสองเมธอดโดยอัตโนมัติ:
วิธี | คำอธิบาย |
---|---|
|
เปรียบเทียบวัตถุปัจจุบันกับวัตถุที่ผ่าน |
|
ส่งกลับรหัสแฮชของวัตถุปัจจุบัน |
ปรากฎว่าวัตถุทุกชิ้นมีequals
วิธีการและสามารถเปรียบเทียบวัตถุประเภทต่างๆได้ โค้ดดังกล่าวจะคอมไพล์และทำงานได้อย่างสมบูรณ์
รหัส | เอาต์พุตคอนโซล |
---|---|
|
|
|
|
3. equals()
วิธีการ
เมธอดequals()
ที่สืบทอดมาจากObject
คลาส ใช้อัลกอริธึมที่ง่ายที่สุดสำหรับการเปรียบเทียบออบเจกต์ปัจจุบันกับอ็อบเจกต์ที่ส่งผ่าน: มันแค่เปรียบเทียบการอ้างอิงไปยังอ็อบเจกต์
คุณจะได้ผลลัพธ์เดียวกันหากคุณเปรียบเทียบPerson
ตัวแปรแทนการเรียกใช้equals()
เมธอด ตัวอย่าง:
รหัส | เอาต์พุตคอนโซล |
---|---|
|
|
เมื่อequals
เรียกใช้เมธอดa
จะเปรียบเทียบการอ้างอิงที่จัดเก็บไว้ในa
ตัวแปรกับการอ้างอิงที่จัดเก็บไว้ในb
ตัวแปร
อย่างไรก็ตาม การเปรียบเทียบจะทำงานแตกต่างกันสำหรับString
ชั้นเรียน ทำไม
เนื่องจากผู้ที่สร้าง คลาสได้เขียน วิธีString
การใช้งานของตนเองequals()
การดำเนินการตามequals()
วิธีการ
ตอนนี้เรามาเขียนวิธีที่เราใช้เท่ากับในPerson
ชั้นเรียน เราจะพิจารณา 4 กรณีหลัก
equals
เมธอด มันจะใช้Object
ออบเจกต์เป็นอาร์กิวเมนต์ เสมอ
สถานการณ์ที่ 1 : อ็อบเจกต์เดียวกันกับที่equals
เรียกใช้เมธอดจะถูกส่งผ่านไปยังequals
เมธอด ด้วย หากการอ้างอิงของวัตถุปัจจุบันและวัตถุที่ผ่านมีค่าเท่ากัน เมธอดจะต้องส่งtrue
คืน วัตถุมีค่าเท่ากับตัวมันเอง
ในโค้ดจะมีลักษณะดังนี้:
รหัส | คำอธิบาย |
---|---|
|
เปรียบเทียบการอ้างอิง |
สถานการณ์ที่ 2 : null
ถูกส่งผ่านไปยังequals
เมธอด — เราไม่มีอะไรจะเปรียบเทียบ วัตถุที่equals
เรียกเมธอดนั้นไม่เป็นโมฆะอย่างแน่นอน ดังนั้นfalse
ในกรณีนี้ เราจำเป็นต้องส่งคืน
ในโค้ดจะมีลักษณะดังนี้:
รหัส | คำอธิบาย |
---|---|
|
เปรียบเทียบการอ้างอิง เป็นวัตถุที่ส่งผ่าน null หรือไม่ |
สถานการณ์ที่ 3 : การอ้างอิงถึงวัตถุที่ไม่ใช่ a Person
จะถูกส่งผ่านไปยังequals
เมธอด วัตถุ มีPerson
ค่าเท่ากับวัตถุที่ไม่ใช่Person
วัตถุหรือไม่? นั่นเป็นคำถามสำหรับนักพัฒนาของPerson
ชั้นเรียนที่จะตัดสินใจว่าเขาหรือเธอต้องการอย่างไร
แต่โดยปกติแล้ววัตถุต้องอยู่ในคลาสเดียวกันจึงจะถือว่าเท่ากัน ดังนั้นหากมีการส่ง ผ่านสิ่งอื่นที่ไม่ใช่วัตถุของPerson
คลาสไปยังวิธีการเท่ากับเราจะส่งคืนเสมอ false
คุณจะตรวจสอบประเภทของวัตถุได้อย่างไร? ถูกต้อง — โดยใช้instanceof
โอเปอเรเตอร์
นี่คือลักษณะของรหัสใหม่ของเรา:
รหัส | คำอธิบาย |
---|---|
|
เปรียบเทียบการอ้างอิง เป็นวัตถุที่ส่งผ่าน null หรือไม่ หากวัตถุที่ผ่านไม่ใช่ Person |
4. การเปรียบเทียบPerson
วัตถุ สองชิ้น
เราจบลงด้วยอะไร ถ้าเราถึงจุดสิ้นสุดของเมธอดแล้ว เราก็มีPerson
การอ้างอิงออบเจกต์ที่null
ไม่ใช่ ดังนั้นเราจึงแปลงเป็น a Person
และเปรียบเทียบข้อมูลภายในที่เกี่ยวข้องของวัตถุทั้งสอง และนั่นคือสถานการณ์ที่สี่ของ เรา
รหัส | คำอธิบาย |
---|---|
|
เปรียบเทียบการอ้างอิง เป็นวัตถุที่ส่งผ่าน null หรือไม่ หากวัตถุที่ผ่านไม่ใช่ Person Typecasting |
และคุณจะเปรียบเทียบสองPerson
วัตถุได้อย่างไร? จะเท่ากันก็ต่อเมื่อมีชื่อ ( name
) และอายุ ( age
) เหมือนกัน รหัสสุดท้ายจะมีลักษณะดังนี้:
รหัส | คำอธิบาย |
---|---|
|
เปรียบเทียบการอ้างอิง เป็นวัตถุที่ส่งผ่าน null หรือไม่ หากวัตถุที่ผ่านไม่ใช่ Person Typecasting |
แต่นั่นไม่ใช่ทั้งหมด
ขั้นแรก ฟิลด์ชื่อคือ a String
ดังนั้นคุณต้องเปรียบเทียบฟิลด์ชื่อโดยเรียกequals
เมธอด
this.name.equals(person.name)
ประการที่สองname
ฟิลด์อาจเป็นnull
: ในกรณีนั้น คุณไม่สามารถเรียกequals
มัน ได้ คุณต้องมีการตรวจสอบเพิ่มเติมสำหรับnull
:
this.name != null && this.name.equals(person.name)
ที่กล่าวว่าหากฟิลด์ชื่ออยู่null
ในPerson
วัตถุทั้งสองชื่อจะยังคงเท่ากัน
รหัสสำหรับสถานการณ์ที่สี่อาจมีลักษณะดังนี้:
|
ถ้าอายุไม่เท่ากัน ทันที return false ถ้า this.name เท่ากับ ก็null ไม่มีประโยชน์ที่จะเปรียบเทียบด้วยequals วิธี นี้ ที่นี่name ฟิลด์ที่สองเท่ากับnull หรือไม่ใช่ เปรียบเทียบฟิลด์ชื่อทั้งสองโดยใช้ equals เมธอด |
5. hashCode()
วิธีการ
นอกจากequals
วิธีการซึ่งมีจุดประสงค์เพื่อทำการเปรียบเทียบโดยละเอียดของฟิลด์ทั้งหมดของวัตถุทั้งสองแล้ว ยังมีวิธีการอื่นที่สามารถใช้สำหรับการเปรียบเทียบที่ไม่แม่นยำแต่รวดเร็วมากhashCode()
:
ลองนึกภาพว่าคุณกำลังจัดเรียงรายการคำนับพันตามตัวอักษร และคุณต้องเปรียบเทียบคู่คำซ้ำๆ และคำนั้นยาวประกอบด้วยตัวอักษรจำนวนมาก โดยทั่วไปแล้วการเปรียบเทียบดังกล่าวจะต้องใช้เวลานานมาก
แต่สามารถเร่งความเร็วได้ สมมติว่าเรามีคำที่ขึ้นต้นด้วยตัวอักษรต่างกัน จะเห็นได้ชัดเจนว่าต่างกัน แต่ถ้าขึ้นต้นด้วยตัวอักษรเดียวกัน เราก็ยังบอกไม่ได้ว่าผลลัพธ์จะเป็นอย่างไร คำอาจออกมาเท่ากันหรือต่างกันก็ได้
วิธี นี้hashCode()
ใช้หลักการที่คล้ายกัน หากคุณเรียกมันบนวัตถุ มันจะส่งกลับ ตัวเลข บางส่วน — คล้ายกับตัวอักษรตัวแรกของคำ หมายเลขนี้มีคุณสมบัติดังต่อไปนี้:
- วัตถุที่เหมือนกันจะมีรหัสแฮชเดียวกันเสมอ
- วัตถุที่แตกต่างกันสามารถมีรหัสแฮชเดียวกัน หรือรหัสแฮชอาจแตกต่างกัน
- หากวัตถุมีแฮชโค้ดต่างกัน แสดงว่าวัตถุนั้นแตกต่างกันอย่างแน่นอน
เพื่อให้ชัดเจนยิ่งขึ้น ลองเปลี่ยนคุณสมบัติเหล่านี้ในแง่ของคำ:
- คำที่เหมือนกันจะมีอักษรตัวแรกเหมือนกันเสมอ
- คำที่ต่างกันอาจมีอักษรตัวแรกเหมือนกัน หรืออักษรตัวแรกอาจต่างกันก็ได้
- หากคำมีอักษรตัวแรกต่างกัน คำนั้นก็จะแตกต่างกันอย่างแน่นอน
คุณสมบัติสุดท้ายใช้เพื่อเร่งการเปรียบเทียบวัตถุ:
ขั้นแรก คำนวณรหัสแฮชของวัตถุทั้งสอง หากแฮชโค้ดเหล่านี้แตกต่างกัน แสดงว่าวัตถุนั้นแตกต่างกันอย่างแน่นอน และไม่จำเป็นต้องเปรียบเทียบเพิ่มเติมอีก
แต่ถ้ารหัสแฮชเหมือนกัน เราก็ยังคงต้องเปรียบเทียบวัตถุโดยใช้วิธีการเท่ากับ
6. สัญญาในรหัส
ลักษณะการทำงานที่อธิบายไว้ข้างต้นต้องนำไปใช้โดยทุกคลาสใน Java ระหว่างการคอมไพล์ ไม่มีวิธีตรวจสอบว่าอ็อบเจ็กต์ถูกเปรียบเทียบอย่างถูกต้องหรือไม่
โปรแกรมเมอร์ Java มีข้อตกลงสากลว่าหากพวกเขาเขียนการใช้งานวิธีการเท่ากับ () ของตนเองและแทนที่การใช้งานมาตรฐาน (ในคลาสObject
) พวกเขาจะต้องเขียนการใช้งานเมธอดของตนเองhashCode()
ในลักษณะที่กฎดังกล่าวข้างต้น พอใจ.
ข้อ ตกลงนี้เรียกว่าสัญญา
หากคุณใช้เฉพาะวิธีequals()
หรือhashCode()
วิธีเดียวในชั้นเรียนของคุณ แสดงว่าคุณละเมิดสัญญาอย่างร้ายแรง (คุณทำผิดข้อตกลง) อย่าทำเช่นนี้
ถ้าโปรแกรมเมอร์คนอื่นใช้โค้ดของคุณ โค้ดนั้นอาจทำงานไม่ถูกต้อง ยิ่งไปกว่านั้น คุณจะใช้รหัสที่อาศัยการปฏิบัติตามสัญญาข้างต้น
เมื่อค้นหาองค์ประกอบ คอลเล็กชัน Java ทั้งหมดจะเปรียบเทียบรหัสแฮชของอ็อบเจ็กต์ก่อน แล้วจึงทำการเปรียบเทียบโดยใช้เมธอดequals
เท่านั้น
นั่นหมายความว่า หากคุณให้equals
เมธอดแก่คลาสของคุณเอง แต่คุณไม่ได้เขียนhashCode()
เมธอดของคุณเอง หรือคุณนำไปใช้อย่างไม่ถูกต้อง การรวบรวมอาจทำงานไม่ถูกต้องกับอ็อบเจกต์ของคุณ
ตัวอย่างเช่น คุณอาจเพิ่มวัตถุลงในรายการแล้วค้นหาโดยใช้contains()
วิธีการ แต่คอลเลกชันอาจไม่พบวัตถุของคุณ
GO TO FULL VERSION