JavaRush /จาวาบล็อก /Random-TH /เท่ากับในการเปรียบเทียบ Java และ String - การเปรียบเทียบส...

เท่ากับในการเปรียบเทียบ Java และ String - การเปรียบเทียบสตริง

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพูดถึงหัวข้อที่สำคัญและน่าสนใจมาก กล่าวคือ การเปรียบเทียบ object ระหว่างกันเท่ากับ()ใน Java และแท้จริงแล้วในกรณีใดบ้างใน Java Object Aจะเท่ากับ Object B ? การเปรียบเทียบเท่ากับและสตริง - 1ลองเขียนตัวอย่าง:
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 ไปที่วัตถุสองชิ้นที่แตกต่างกันไปยังที่อยู่ที่แตกต่างกันสองแห่ง ลองนึกภาพสถานการณ์ด้วยการเปรียบเทียบผู้คน คงมีคนในโลกนี้ที่มีชื่อ สีตา อายุ ส่วนสูง สีผม ฯลฯ เช่นเดียวกับคุณ นั่นคือคุณมีความคล้ายคลึงกันหลายประการ แต่คุณก็ยังไม่ใช่ฝาแฝด และโดยเฉพาะอย่างยิ่งไม่ใช่คนคนเดียวกัน การเปรียบเทียบเท่ากับและสตริง - 2ตัวดำเนินการใช้ตรรกะเดียวกันโดยประมาณ==เมื่อเราใช้เพื่อเปรียบเทียบวัตถุสองชิ้น แต่ถ้าคุณต้องการตรรกะที่แตกต่างกันในโปรแกรมของคุณล่ะ? เช่น ถ้าโปรแกรมของคุณจำลองการวิเคราะห์ 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) การเปรียบเทียบเท่ากับและสตริง - 3พูลสตริงเป็นพื้นที่สำหรับจัดเก็บค่าสตริงทั้งหมดที่คุณสร้างในโปรแกรมของคุณ มันถูกสร้างขึ้นมาเพื่ออะไร? ตามที่กล่าวไว้ข้างต้น สตริงกินพื้นที่ส่วนใหญ่ของออบเจ็กต์ทั้งหมด ในโปรแกรมขนาดใหญ่ใดๆ จะมีการสร้างบรรทัดจำนวนมาก เพื่อประหยัดหน่วยความจำนี่คือสิ่งที่จำเป็น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!");
   }
}
ในหน่วยความจำจะมีลักษณะดังนี้: การเปรียบเทียบเท่ากับและสตริง - 4และทุกครั้งที่มีการสร้างวัตถุใหม่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()ชี้ไปที่พื้นที่เดียวกันในหน่วยความจำ และแน่นอนว่าทั้งคู่ทำเช่นนั้น :) เพื่อสรุป จดจำ และใช้กฎหลัก: ในการเปรียบเทียบสตริง ให้ ใช้ เสมอเท่ากับ() วิธี! เมื่อเปรียบเทียบสตริง คุณมักจะหมายถึงการเปรียบเทียบข้อความ ไม่ใช่ลิงก์ พื้นที่หน่วยความจำ และอื่นๆ วิธีการเท่ากับ () ทำสิ่งที่คุณต้องการอย่างแน่นอน นี่คือลิงค์บางส่วนให้คุณศึกษาด้วยตัวเอง:
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION