สวัสดี! วันนี้เราจะพูดถึงหัวข้อที่สำคัญและน่าสนใจมาก กล่าวคือ การเปรียบเทียบ object ระหว่างกันเท่ากับ()ใน Java และแท้จริงแล้วในกรณีใดบ้างใน Java Object Aจะเท่ากับ Object B ?
ลองเขียนตัวอย่าง:
ตัวดำเนินการใช้ตรรกะเดียวกันโดยประมาณ
พูลสตริงเป็นพื้นที่สำหรับจัดเก็บค่าสตริงทั้งหมดที่คุณสร้างในโปรแกรมของคุณ มันถูกสร้างขึ้นมาเพื่ออะไร? ตามที่กล่าวไว้ข้างต้น สตริงกินพื้นที่ส่วนใหญ่ของออบเจ็กต์ทั้งหมด ในโปรแกรมขนาดใหญ่ใดๆ จะมีการสร้างบรรทัดจำนวนมาก เพื่อประหยัดหน่วยความจำนี่คือสิ่งที่จำเป็น
และทุกครั้งที่มีการสร้างวัตถุใหม่
public class Car {
String model;
int maxSpeed;
public static void main(String[] args) {
Car car1 = new Car();
car1.model = "Ferrari";
car1.maxSpeed = 300;
Car car2 = new Car();
car2.model = "Ferrari";
car2.maxSpeed = 300;
System.out.println(car1 == car2);
}
}
เอาต์พุตคอนโซล:
false
โอเค หยุด ทำไมรถสองคันนี้ถึงไม่เท่ากันจริงๆ? เราให้คุณสมบัติเดียวกันแก่พวกเขา แต่ผลลัพธ์ของการเปรียบเทียบนั้นเป็นเท็จ คำตอบนั้นง่าย ตัวดำเนินการ==
ไม่ใช่การเปรียบเทียบคุณสมบัติของวัตถุ แต่เป็นการเปรียบเทียบลิงก์ แม้ว่าวัตถุสองชิ้นจะมีคุณสมบัติเหมือนกัน 500 รายการ ผลลัพธ์ของการเปรียบเทียบยังคงเป็นเท็จ ท้ายที่สุดแล้ว ลิงก์car1
จะชี้car2
ไปที่วัตถุสองชิ้นที่แตกต่างกันไปยังที่อยู่ที่แตกต่างกันสองแห่ง ลองนึกภาพสถานการณ์ด้วยการเปรียบเทียบผู้คน คงมีคนในโลกนี้ที่มีชื่อ สีตา อายุ ส่วนสูง สีผม ฯลฯ เช่นเดียวกับคุณ นั่นคือคุณมีความคล้ายคลึงกันหลายประการ แต่คุณก็ยังไม่ใช่ฝาแฝด และโดยเฉพาะอย่างยิ่งไม่ใช่คนคนเดียวกัน 
==
เมื่อเราใช้เพื่อเปรียบเทียบวัตถุสองชิ้น แต่ถ้าคุณต้องการตรรกะที่แตกต่างกันในโปรแกรมของคุณล่ะ? เช่น ถ้าโปรแกรมของคุณจำลองการวิเคราะห์ DNA เธอต้องเปรียบเทียบรหัส DNA ของคนสองคนและตัดสินว่าพวกเขาเป็นฝาแฝดกัน
public class Man {
int dnaCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = 1111222233;
Man man2 = new Man();
man2.dnaCode = 1111222233;
System.out.println(man1 == man2);
}
}
เอาต์พุตคอนโซล:
false
มันสมเหตุสมผลที่ผลลัพธ์จะเหมือนเดิม (ท้ายที่สุดเราไม่ได้เปลี่ยนแปลงอะไรเลย) แต่ตอนนี้เราไม่พอใจกับมัน! แท้จริงแล้วในชีวิตจริง การวิเคราะห์ DNA คือการรับประกันร้อยเปอร์เซ็นต์ว่าเรากำลังเผชิญกับฝาแฝด แต่โปรแกรมและผู้ดำเนินการของเรา==
บอกเราเป็นอย่างอื่น เราจะเปลี่ยนพฤติกรรมนี้ได้อย่างไร และมั่นใจได้อย่างไรว่าถ้าผลตรวจ DNA ตรงกัน โปรแกรมก็จะให้ผลลัพธ์ที่ถูกต้อง เพื่อจุดประสงค์นี้ วิธีการพิเศษถูกสร้างขึ้นใน Java - เท่ากับ( )
วิธีการเท่ากับ () ใน Java
เช่นเดียวกับวิธีtoString()
ที่เราพูดคุยกันก่อนหน้านี้เท่ากับ()อยู่ในคลาส ซึ่งเป็นObject
คลาสที่สำคัญที่สุดใน Java ซึ่งคลาสอื่นทั้งหมดได้มาจากคลาสอื่นทั้งหมด อย่างไรก็ตาม ตัวเท่ากับ() จะไม่เปลี่ยนพฤติกรรมของโปรแกรมของเราในทางใดทางหนึ่ง:
public class Man {
String dnaCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = "111122223333";
Man man2 = new Man();
man2.dnaCode = "111122223333";
System.out.println(man1.equals(man2));
}
}
เอาต์พุตคอนโซล:
false
ผลลัพธ์ก็เหมือนกันทุกประการ แล้วเหตุใดจึงต้องใช้วิธีนี้? :/ มันง่าย. ความจริงก็คือตอนนี้เราใช้วิธีนี้ตามที่นำมาใช้ในชั้นเรียนObject
นั่นเอง และถ้าเราเข้าไปในโค้ดของชั้นเรียนObject
และดูว่าวิธีการนี้ถูกนำมาใช้อย่างไรและมันทำอะไร เราจะเห็นว่า:
public boolean equals(Object obj) {
return (this == obj);
}
นี่คือเหตุผลว่าทำไมพฤติกรรมของโปรแกรมของเราจึงไม่เปลี่ยนแปลง! ภายในเมธอดเท่ากับ () ของคลาสจะObject
มีการเปรียบเทียบการอ้างอิงเดียวกัน ==
แต่เคล็ดลับของวิธีนี้คือเราสามารถแทนที่มันได้ การเอาชนะหมายถึงการเขียนเมธอดเท่ากับ () ของคุณเองในชั้นเรียนของเราMan
และทำให้มันเป็นไปในแบบที่เราต้องการ! ตอนนี้เราไม่พอใจที่เช็คman1.equals(man2)
ทำสิ่งเดียวกันman1 == man2
กับ นี่คือสิ่งที่เราจะทำในสถานการณ์นี้:
public class Man {
int dnaCode;
public boolean equals(Man man) {
return this.dnaCode == man.dnaCode;
}
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = 1111222233;
Man man2 = new Man();
man2.dnaCode = 1111222233;
System.out.println(man1.equals(man2));
}
}
เอาต์พุตคอนโซล:
true
ผลลัพธ์ที่แตกต่างไปจากเดิมอย่างสิ้นเชิง! ด้วยการเขียนวิธีเท่ากับ() ของเราเองแทนวิธีมาตรฐาน เราได้บรรลุพฤติกรรมที่ถูกต้อง: ตอนนี้ถ้าคนสองคนมีรหัส DNA เหมือนกัน โปรแกรมจะบอกเราว่า: “การวิเคราะห์ DNA แสดงให้เห็นว่าพวกเขาเป็นฝาแฝด” และคืนค่าเป็นจริง! ด้วยการแทนที่เมธอดเท่ากับ() ในคลาสของคุณ คุณสามารถสร้างตรรกะการเปรียบเทียบวัตถุที่จำเป็นได้อย่างง่ายดาย เราได้สัมผัสถึงการเปรียบเทียบวัตถุในแง่ทั่วไปเท่านั้น เราจะยังคงมีการบรรยาย ขนาดใหญ่แยกต่างหาก ในหัวข้อนี้ข้างหน้า (คุณสามารถอ่านได้อย่างรวดเร็วหากสนใจ)
การเปรียบเทียบสตริงใน Java - การเปรียบเทียบสตริง
เหตุใดเราจึงปฏิบัติต่อการเปรียบเทียบสตริงแยกจากทุกสิ่ง? จริงๆ แล้ว บรรทัดในการเขียนโปรแกรมเป็นเรื่องราวที่แตกต่างไปจากเดิมอย่างสิ้นเชิง ประการแรก หากคุณใช้โปรแกรม Java ทั้งหมดที่มนุษย์เขียนขึ้น ประมาณ 25% ของวัตถุในนั้นจะประกอบด้วยโปรแกรมเหล่านั้น ดังนั้นหัวข้อนี้จึงมีความสำคัญมาก ประการที่สอง กระบวนการเปรียบเทียบสตริงค่อนข้างแตกต่างจากวัตถุอื่นๆ ลองดูตัวอย่างง่ายๆ:public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1 == s2);
}
}
เอาต์พุตคอนโซล:
false
แต่ทำไมถึงเป็นเท็จ? เส้นเหมือนกันทุกประการ คำต่อคำ :/ คุณสามารถสันนิษฐานได้ว่านี่เป็นเพราะตัวดำเนินการ ==
เปรียบเทียบการอ้างอิง! ท้ายที่สุดแล้วs1
พวกเขาs2
มีที่อยู่ต่างกันในหน่วยความจำ หากความคิดนี้เกิดขึ้นกับคุณ เรามาทำซ้ำตัวอย่างของเรา:
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = "JavaRush is the best site to learn Java!";
System.out.println(s1 == s2);
}
}
ตอนนี้เรามีลิงก์สองลิงก์ด้วย แต่ผลลัพธ์เปลี่ยนไปตรงกันข้าม: เอาต์พุตคอนโซล:
true
สับสนไปหมด? :) ลองคิดดูสิ โอเปอเรเตอร์==
จะเปรียบเทียบที่อยู่ในหน่วยความจำจริงๆ กฎนี้ใช้ได้ผลเสมอและไม่ต้องสงสัยเลย ซึ่งหมายความว่าหากs1 == s2
คืนค่าเป็นจริง สตริงทั้งสองนี้จะมีที่อยู่เดียวกันในหน่วยความจำ และก็เป็นเช่นนั้นจริงๆ! ถึงเวลาทำความคุ้นเคยกับพื้นที่หน่วยความจำพิเศษสำหรับจัดเก็บสตริง - พูลสตริง ( String pool
) 
String Pool
- บรรทัดที่มีข้อความที่คุณต้องการอยู่ที่นั่นและในอนาคตลิงก์ที่สร้างขึ้นใหม่จะอ้างถึงพื้นที่หน่วยความจำเดียวกันโดยไม่จำเป็นต้องจัดสรรหน่วยความจำเพิ่มเติมในแต่ละครั้ง ทุกครั้งที่คุณเขียนString = “........”
โปรแกรมจะตรวจสอบว่ามีบรรทัดที่มีข้อความดังกล่าวอยู่ในสตริงพูลหรือไม่ หากมีจะไม่สร้างอันใหม่ และลิงก์ใหม่จะชี้ไปยังที่อยู่เดียวกันในกลุ่มสตริงที่จัดเก็บสตริงนี้ ดังนั้นเมื่อเราเขียนลงในโปรแกรม
String s1 = "JavaRush is the best site to learn Java!";
String s2 = "JavaRush is the best site to learn Java!";
ลิงก์s2
จะชี้ไปยังตำแหน่งเดียวกันs1
กับ คำสั่งแรกสร้างบรรทัดใหม่ในพูลสตริงพร้อมข้อความที่เราต้องการ และเมื่อมาถึงคำสั่งที่สอง มันก็จะอ้างถึงพื้นที่หน่วยความจำเดียวกันกับs1
. คุณสามารถสร้างบรรทัดด้วยข้อความเดียวกันได้อีกอย่างน้อย 500 บรรทัด ผลลัพธ์จะไม่เปลี่ยนแปลง หยุด. แต่เหตุใดตัวอย่างนี้จึงไม่เหมาะกับเราก่อนหน้านี้
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1 == s2);
}
}
ฉันคิดว่าโดยสัญชาตญาณคุณเดาแล้วว่าสาเหตุคืออะไร :) ลองเดาก่อนอ่านต่อ คุณจะเห็นว่าสองบรรทัดนี้ถูกสร้างขึ้นแตกต่างกัน อันแรกได้รับความช่วยเหลือจากโอเปอเรเตอร์new
และอันที่สองไม่มีมัน นี่คือเหตุผลที่แม่นยำ ตัวดำเนินการใหม่เมื่อสร้างวัตถุจะบังคับให้จัดสรรพื้นที่ใหม่ในหน่วยความจำสำหรับวัตถุนั้น และบรรทัดที่สร้างด้วยnew
ไม่ได้ลงท้ายด้วยString Pool
: มันจะกลายเป็นวัตถุที่แยกจากกัน แม้ว่าข้อความจะเหมือนกับบรรทัดเดียวกันจากString Pool
'a ทุกประการก็ตาม นั่นคือถ้าเราเขียนโค้ดต่อไปนี้:
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = "JavaRush is the best site to learn Java!";
String s3 = new String("JavaRush is the best site to learn Java!");
}
}
ในหน่วยความจำจะมีลักษณะดังนี้: 
new
พื้นที่ใหม่จะถูกจัดสรรในหน่วยความจำ แม้ว่าข้อความในบรรทัดใหม่จะเหมือนกันก็ตาม! ดูเหมือนเราจะแยก ตัวดำเนินการ ออกแล้ว ==
แต่เพื่อนใหม่ของเรา - วิธีเท่ากับ() ล่ะ?
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1.equals(s2));
}
}
เอาต์พุตคอนโซล:
true
น่าสนใจ. เรารู้แน่ชัดว่าอะไรs1
และs2
ชี้ไปยังส่วนต่างๆ ในหน่วยความจำ แต่อย่างไรก็ตาม วิธีการเท่ากับ() บอกว่าพวกมันเท่ากัน ทำไม โปรดจำไว้ว่า ข้างต้นเราได้กล่าวไว้ว่าสามารถแทนที่เมธอดเท่ากับ () ในคลาสของคุณได้ เพื่อที่จะเปรียบเทียบอ็อบเจ็กต์ตามที่คุณต้องการ String
นั่นคือสิ่งที่ พวก เขาทำ กับชั้นเรียน มันมีวิธีการแทนที่เท่ากับ () และมันไม่ได้เปรียบเทียบลิงก์ แต่เป็นการเปรียบเทียบลำดับของอักขระในสตริง และหากข้อความในสตริงเหมือนกัน ก็ไม่สำคัญว่าข้อความจะถูกสร้างขึ้นอย่างไรและจัดเก็บไว้ที่ไหน: ในกลุ่มสตริงหรือในพื้นที่หน่วยความจำแยกต่างหาก ผลลัพธ์ของการเปรียบเทียบจะเป็นจริง อย่างไรก็ตาม Java ช่วยให้คุณสามารถเปรียบเทียบสตริงได้อย่างถูกต้องโดยไม่คำนึงถึงขนาดตัวพิมพ์ ในสถานการณ์ปกติ หากคุณเขียนบรรทัดใดบรรทัดหนึ่ง เช่น เป็นตัวพิมพ์ใหญ่ ผลลัพธ์ของการเปรียบเทียบจะเป็นเท็จ:
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JAVARUSH - ЛУЧШИЙ САЙТ ДЛЯ ИЗУЧЕНИЯ JAVA!");
System.out.println(s1.equals(s2));
}
}
เอาต์พุตคอนโซล:
false
สำหรับกรณี นี้ คลาสString
จะมีเมธอด equalsIgnoreCase()
หากสิ่งสำคัญในการเปรียบเทียบของคุณคือลำดับของอักขระเฉพาะและไม่ใช่ตัวพิมพ์คุณสามารถใช้มันได้ ตัวอย่างเช่น สิ่งนี้จะมีประโยชน์เมื่อเปรียบเทียบที่อยู่อีเมลสองรายการ:
public class Main {
public static void main(String[] args) {
String address1 = "Moscow, Academician Korolev street, 12";
String address2 = new String("Г. МОСКВА, УЛ. АКАДЕМИКА КОРОЛЕВА, ДОМ 12");
System.out.println(address1.equalsIgnoreCase(address2));
}
}
ในกรณีนี้เห็นได้ชัดว่าเรากำลังพูดถึงที่อยู่เดียวกัน ดังนั้นการใช้วิธีนี้equalsIgnoreCase()
จะเป็นการตัดสินใจที่ถูกต้อง
วิธีการ String.intern()
ชั้นเรียนString
มีวิธีที่ยุ่งยากอีกวิธีหนึ่ง - intern()
; วิธีการนี้intern()
ใช้ได้กับString Pool
'om โดยตรง หากคุณเรียกใช้เมธอดintern()
บนสตริง มันจะ:
- ดูว่ามีสตริงที่มีข้อความนี้อยู่ในพูลสตริงหรือไม่
- หากมี ให้ส่งคืนลิงก์ไปยังพูล
- ถ้าไม่เช่นนั้น ระบบจะวางบรรทัดพร้อมข้อความนี้ในสตริงพูลและส่งกลับลิงก์ไปยังข้อความนั้น
intern()
กับการอ้างอิงสตริงที่สร้างขึ้นผ่านทาง new เราสามารถเปรียบเทียบกับการอ้างอิงสตริงจากString Pool
'a ผ่าน==
.
public class Main {
public static void main(String[] args) {
String s1 = "JavaRush is the best site to learn Java!";
String s2 = new String("JavaRush is the best site to learn Java!");
System.out.println(s1 == s2.intern());
}
}
เอาต์พุตคอนโซล:
true
ก่อนหน้านี้ เมื่อเราเปรียบเทียบพวกมันโดยไม่มีintern()
ผลลัพธ์ที่ได้คือเท็จ ตอนนี้วิธีintern()
การตรวจสอบว่ามีบรรทัดที่มีข้อความ"JavaRush - ไซต์ที่ดีที่สุดสำหรับการเรียนรู้ Java!" หรือไม่ ในสระสตริง แน่นอนว่ามันอยู่ที่นั่น เราสร้างมันขึ้นมาตอนที่เราเขียน
String s1 = "JavaRush is the best site to learn Java!";
มีการตรวจสอบว่าการอ้างอิงs1
และการอ้างอิงที่ส่งคืนโดยเมธอดs2.intern()
ชี้ไปที่พื้นที่เดียวกันในหน่วยความจำ และแน่นอนว่าทั้งคู่ทำเช่นนั้น :) เพื่อสรุป จดจำ และใช้กฎหลัก: ในการเปรียบเทียบสตริง ให้ ใช้ เสมอเท่ากับ() วิธี! เมื่อเปรียบเทียบสตริง คุณมักจะหมายถึงการเปรียบเทียบข้อความ ไม่ใช่ลิงก์ พื้นที่หน่วยความจำ และอื่นๆ วิธีการเท่ากับ () ทำสิ่งที่คุณต้องการอย่างแน่นอน นี่คือลิงค์บางส่วนให้คุณศึกษาด้วยตัวเอง:
GO TO FULL VERSION