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