CodeGym /จาวาบล็อก /สุ่ม /เปรียบเทียบการเปรียบเทียบสตริงและเท่ากับใน Java
John Squirrels
ระดับ
San Francisco

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

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพูดถึงหัวข้อที่สำคัญและน่าสนใจคือการเปรียบเทียบวัตถุกับวัตถุ (เปรียบเทียบสตริงและเท่ากับ) ดังนั้นใน Java เมื่อใดที่วัตถุAจะเท่ากับวัตถุ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);
   }
}
เอาต์พุตคอนโซล: เท็จ รอหยุด ทำไมรถสองคันนี้มันไม่เท่ากัน? เรากำหนดคุณสมบัติเดียวกันให้พวกเขา แต่ผลลัพธ์ของการเปรียบเทียบเป็นเท็จ คำตอบนั้นง่าย ตัว ดำเนินการ ==เปรียบเทียบการอ้างอิงวัตถุ ไม่ใช่คุณสมบัติของวัตถุ ออบเจ็กต์สองรายการอาจมีฟิลด์ 500 ฟิลด์ที่มีค่าเหมือนกัน แต่การเปรียบเทียบพวกมันจะยังคงให้ผลเป็นเท็จ ท้ายที่สุดแล้ว อ้างอิงถึงcar1และcar2ชี้ไปที่วัตถุสองชิ้นที่ต่างกัน นั่นคือสองที่อยู่ที่แตกต่างกัน ลองนึกภาพสถานการณ์ที่คุณกำลังเปรียบเทียบผู้คน แน่นอน ที่ไหนสักแห่งในโลก มีคนๆ ​​หนึ่งที่ใช้ชื่อเดียวกับคุณ สีตา อายุ ส่วนสูง สีผม ฯลฯ นั่นทำให้คุณคล้ายกันในหลายๆ ด้าน แต่คุณก็ยังไม่ใช่ฝาแฝด — และแน่นอนว่าคุณไม่ใช่ คนเดียวกัน
เท่ากับและการเปรียบเทียบสตริง - 2
ตัว ดำเนินการ ==ใช้ตรรกะเดียวกันนี้โดยประมาณเมื่อเราใช้เพื่อเปรียบเทียบวัตถุสองชิ้น แต่ถ้าคุณต้องการให้โปรแกรมของคุณใช้ตรรกะที่แตกต่างกันล่ะ ตัวอย่างเช่น สมมติว่าโปรแกรมของคุณทำการวิเคราะห์ดีเอ็นเอ เป็นการเปรียบเทียบรหัสพันธุกรรมของคนสองคน และตัดสินว่าพวกเขาเป็นฝาแฝดหรือไม่

public class Man {

   int geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = 1111222233;

       Man man2 = new Man();
       man2.geneticCode = 1111222233;

       System.out.println(man1 == man2);
   }
}
เอาต์พุตของคอนโซล: เท็จ เราได้รับผลลัพธ์เชิงตรรกะเหมือนกัน (เพราะเราไม่ได้เปลี่ยนแปลงอะไรมาก) แต่ตอนนี้ตรรกะนั้นไม่ดีแล้ว! ท้ายที่สุดแล้ว ในชีวิตจริง การวิเคราะห์ดีเอ็นเอควรรับประกันได้ 100% ว่าเรามีฝาแฝดยืนอยู่ตรงหน้าเรา แต่โปรแกรมของเราและ ตัวดำเนินการ ==บอกเราตรงกันข้าม เราจะเปลี่ยนพฤติกรรมนี้ได้อย่างไรและทำให้แน่ใจว่าโปรแกรมให้ผลลัพธ์ที่ถูกต้องเมื่อ DNA ตรงกัน Java มีเมธอดพิเศษสำหรับสิ่งนี้: equals () เช่นเดียวกับ เมธอด toString()ที่เราพูดถึงไปก่อนหน้านี้equals()อยู่ใน คลาส Objectซึ่งเป็นคลาสที่สำคัญที่สุดใน Java ซึ่งเป็นคลาสที่คลาสอื่นๆ สืบทอดมา แต่เท่ากับ ()ไม่ได้เปลี่ยนพฤติกรรมของโปรแกรมของเราทั้งหมด:

public class Man {

   String geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = "111122223333";

       Man man2 = new Man();
       man2.geneticCode = "111122223333";

       System.out.println(man1.equals(man2));
   }
}
เอาต์พุตคอนโซล: เท็จ ผลลัพธ์เดียวกันทุกประการ ดังนั้นเราต้องการวิธีนี้เพื่ออะไร :/ มันง่ายทั้งหมด ปัญหาที่นี่คือเรากำลังใช้วิธีนี้ในขณะที่นำไปใช้ในคลาสวัตถุ และถ้าเราเข้าไปในโค้ดของ คลาส Objectและดูที่การใช้งานของเมธอด นี่คือสิ่งที่เราจะเห็น:

public boolean equals(Object obj) {
   return (this == obj);
}
นั่นเป็นเหตุผลที่พฤติกรรมของโปรแกรมไม่เปลี่ยนแปลง! ตัวดำเนินการ ==ที่เหมือนกันมาก(ซึ่งเปรียบเทียบการอ้างอิง) ถูกใช้ภายใน วิธี การเท่ากับ ()ของคลาสวัตถุ แต่เคล็ดลับด้วยวิธีนี้คือเราสามารถแทนที่มันได้ การแทนที่หมายถึงการเขียน วิธี การเท่ากับ () ของคุณเอง ใน คลาส 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)); 

   } 
} 
เอาต์พุตของคอนโซล: จริง ตอนนี้เราได้ผลลัพธ์ที่แตกต่างไปจากเดิมอย่างสิ้นเชิง! โดยการเขียนวิธี การเท่ากับ ()ของเราเองและใช้มันแทนวิธีมาตรฐาน เราได้สร้างพฤติกรรมที่ถูกต้อง: ตอนนี้ถ้าคนสองคนมี DNA เหมือนกัน โปรแกรมจะรายงานว่า "การวิเคราะห์ DNA ได้พิสูจน์แล้วว่าพวกเขาเป็นฝาแฝด" และส่งกลับค่าจริง! ด้วยการลบล้าง วิธี การเท่ากับ ()ในคลาสของคุณ คุณสามารถสร้างตรรกะการเปรียบเทียบวัตถุที่คุณต้องการได้อย่างง่ายดาย ในความเป็นจริง เราเพิ่งสัมผัสการเปรียบเทียบวัตถุเท่านั้น ข้างหน้าเรายังมีบทเรียนแบบสแตนด์อโลนขนาดใหญ่ในหัวข้อนี้ (คุณอ่านตอนนี้หากคุณสนใจ)

การเปรียบเทียบสตริงใน Java

เหตุใดเราจึงพิจารณาการเปรียบเทียบสตริงแยกต่างหากจากอย่างอื่น ความจริงก็คือสตริงเป็นหัวข้อในการเขียนโปรแกรม อย่างแรก ถ้าคุณใช้โปรแกรม Java ทั้งหมดที่เคยเขียนมา คุณจะพบว่าประมาณ 25% ของอ็อบเจกต์ในนั้นเป็นสตริง หัวข้อนี้จึงสำคัญมาก ประการที่สอง กระบวนการเปรียบเทียบสตริงนั้นแตกต่างจากวัตถุอื่นมาก ลองพิจารณาตัวอย่างง่ายๆ:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
เอาต์พุตคอนโซล: เท็จ แต่ทำไมเราถึงได้รับเท็จ ท้ายที่สุดแล้ว สตริงจะเหมือนกันทุกประการ คำต่อคำ :/ คุณอาจเดาสาเหตุได้: เป็นเพราะ ตัวดำเนินการ ==เปรียบเทียบการอ้างอิง ! เห็นได้ชัดว่าs1และs2มีที่อยู่ในหน่วยความจำต่างกัน หากคุณคิดอย่างนั้น ลองทำตัวอย่างของเราใหม่:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       System.out.println(s1 == s2);
   }
}
ตอนนี้เรามีการอ้างอิงสองครั้งอีกครั้ง แต่ผลลัพธ์กลับตรงกันข้าม: เอาต์พุตของคอนโซล: จริง สับสนอย่างช่วยไม่ได้? มาดูกันว่าเกิดอะไรขึ้น ตัว ดำเนินการ ==เปรียบเทียบที่อยู่หน่วยความจำจริงๆ นี่เป็นความจริงเสมอและคุณไม่จำเป็นต้องสงสัย นั่นหมายความว่า ถ้าs1 == s2คืนค่าเป็น true แสดงว่าสตริงทั้งสองนี้มีแอดเดรสเดียวกัน และนี่คือความจริง! ได้เวลาแนะนำพื้นที่หน่วยความจำพิเศษสำหรับการจัดเก็บสตริงให้คุณแล้ว: พูลสตริง
เท่ากับและการเปรียบเทียบสตริง - 3
สระสตริงเป็นพื้นที่สำหรับจัดเก็บค่าสตริงทั้งหมดที่คุณสร้างขึ้นในโปรแกรมของคุณ ทำไมมันถูกสร้างขึ้น? อย่างที่เราพูดไปก่อนหน้านี้ สตริงเป็นตัวแทนของออบเจกต์ทั้งหมดเป็นเปอร์เซ็นต์จำนวนมาก โปรแกรมขนาดใหญ่ใด ๆ จะสร้างสตริงจำนวนมาก สตริงพูลถูกสร้างขึ้นเพื่อบันทึกหน่วยความจำ: สตริงจะถูกวางไว้ที่นั่น จากนั้นสตริงที่สร้างขึ้นในภายหลังจะอ้างถึงพื้นที่เดียวกันของหน่วยความจำ ไม่จำเป็นต้องจัดสรรหน่วยความจำเพิ่มเติมในแต่ละครั้ง ทุกครั้งที่คุณเขียนString = "......."โปรแกรมจะตรวจสอบว่ามีสตริงที่เหมือนกันในกลุ่มสตริงหรือไม่ หากมี จะไม่สร้างสตริงใหม่ และการอ้างอิงใหม่จะชี้ไปยังที่อยู่เดียวกันในกลุ่มสตริง (ซึ่งมีสตริงที่เหมือนกันอยู่) ดังนั้นเมื่อเราเขียน

String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2ชี้ไปที่เดียวกับs1 คำสั่งแรกสร้างสตริงใหม่ในกลุ่มสตริง คำสั่งที่สองหมายถึงพื้นที่หน่วยความจำเดียวกันกับs1 คุณสามารถสร้างสตริงที่เหมือนกันได้อีก 500 สายและผลลัพธ์จะไม่เปลี่ยนแปลง รอสักครู่. หากเป็นเช่นนั้นจริง เหตุใดตัวอย่างนี้จึงไม่ได้ผลมาก่อน

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
ฉันคิดว่าสัญชาตญาณของคุณได้บอกเหตุผลแล้ว =) ลองเดาก่อนอ่านต่อไป คุณจะเห็นว่าสตริงทั้งสองนี้ถูกประกาศด้วยวิธีที่ต่างกัน อันหนึ่งมีโอเปอเรเตอร์ใหม่ และอีกอันไม่มี นี่คือเหตุผล เมื่อ โอเปอเรเตอร์ ใหม่ถูกใช้เพื่อสร้างออบเจกต์ มันจะจัดสรรพื้นที่หน่วยความจำใหม่ให้กับออบเจกต์อย่างแข็งขัน และสตริงที่สร้างขึ้นโดยใช้newจะไม่จบลงในกลุ่มสตริง — มันจะกลายเป็นออบเจกต์แยกต่างหาก แม้ว่าข้อความของสตริงจะตรงกับสตริงในกลุ่มสตริงอย่างสมบูรณ์ก็ตาม นั่นคือถ้าเราเขียนรหัสต่อไปนี้:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       String s3 = new String("CodeGym is the best website for learning Java!");
   }
}
ในหน่วยความจำดูเหมือนว่า:
การเปรียบเทียบเท่ากับและสตริง - 4
และทุกครั้งที่คุณสร้างวัตถุใหม่โดยใช้newพื้นที่หน่วยความจำใหม่จะถูกจัดสรร แม้ว่าข้อความภายในสตริงใหม่จะเหมือนกันก็ตาม! ดูเหมือนว่าเราได้หาตัวดำเนินการ== แล้ว แต่คนรู้จักใหม่ของเรา วิธีการ เท่ากับ () ล่ะ ?

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1.equals(s2));
   }
}
เอาต์พุตคอนโซล: จริง น่าสนใจ เรามั่นใจว่าs1และs2ชี้ไปยังพื้นที่ต่างๆ ในหน่วยความจำ แต่ วิธีการ เท่ากับ ()ยังคงบอกเราว่าพวกเขาเท่ากัน ทำไม โปรดจำไว้ว่าก่อนหน้านี้เรากล่าวว่า วิธีการ เท่ากับ ()สามารถแทนที่เพื่อเปรียบเทียบวัตถุตามที่เราต้องการได้หรือไม่? นั่นเป็นเพียงสิ่งที่พวกเขาทำกับคลาสString มันแทนที่เท่ากับ ()วิธี. และแทนที่จะเปรียบเทียบการอ้างอิง จะเปรียบเทียบลำดับของอักขระในสตริง หากข้อความเหมือนกัน ไม่สำคัญว่าข้อความนั้นถูกสร้างขึ้นอย่างไรหรือเก็บไว้ที่ใด ไม่ว่าจะอยู่ในกลุ่มสตริงหรือพื้นที่หน่วยความจำแยกต่างหาก ผลของการเปรียบเทียบจะเป็นจริง อย่างไรก็ตาม Java ให้คุณทำการเปรียบเทียบสตริงโดยไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ โดยปกติ หากสตริงใดสตริงหนึ่งมีตัวพิมพ์ใหญ่ทั้งหมด ผลลัพธ์ของการเปรียบเทียบจะเป็นเท็จ:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CODEGYM IS THE BEST WEBSITE FOR LEARNING JAVA!");
       System.out.println(s1.equals(s2));
   }
}
เอาต์พุตของคอนโซล: เท็จ สำหรับการเปรียบเทียบที่ไม่คำนึงถึงขนาด ตัว พิมพ์ คลาสสตริงมีเมธอดequalsIgnoreCase() คุณสามารถใช้ได้หากคุณสนใจแค่การเปรียบเทียบลำดับของอักขระเฉพาะมากกว่าตัวอักษรตัวพิมพ์ใหญ่ ตัวอย่างเช่น สิ่งนี้อาจมีประโยชน์เมื่อเปรียบเทียบที่อยู่สองแห่ง:

public class Main {

   public static void main(String[] args) {

       String address1 = "2311 Broadway Street, San Francisco";
       String address2 = new String("2311 BROADWAY STREET, SAN FRANCISCO");
       System.out.println(address1.equalsIgnoreCase(address2));
   }
}
ในกรณีนี้ เห็นได้ชัดว่าเรากำลังพูดถึงที่อยู่เดียวกัน ดังนั้นจึงเหมาะสมที่จะใช้เมธอดequalsIgnoreCase()

วิธีการ String.intern()

คลาสStringมีเมธอดที่ยุ่งยากอีกวิธีหนึ่ง: intern() ; วิธีการฝึกงาน ()ทำงานโดยตรงกับพูลสตริง หากคุณเรียก วิธี การฝึกงาน ()ในบางสตริง:
  • ตรวจสอบว่ามีสตริงที่ตรงกันในกลุ่มสตริงหรือไม่
  • ถ้ามี มันจะส่งกลับการอ้างอิงไปยังสตริงในกลุ่ม
  • ถ้าไม่ มันจะเพิ่มสตริงลงในกลุ่มสตริงและส่งกลับการอ้างอิงถึงมัน
หลังจากใช้ เมธอด intern()ในการอ้างอิงสตริงที่ได้รับโดยใช้newเราสามารถใช้ ตัวดำเนินการ ==เพื่อเปรียบเทียบกับการอ้างอิงสตริงจากกลุ่มสตริง

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2.intern());
   }
}
เอาต์พุตคอนโซล: จริง เมื่อเราเปรียบเทียบสตริงเหล่านี้ก่อนหน้านี้โดยไม่มีการฝึกงาน ()ผลลัพธ์จะเป็นเท็จ ตอนนี้ วิธี การฝึกงาน ()ตรวจสอบว่าสตริง"CodeGym เป็นไซต์ที่ดีที่สุดสำหรับการเรียนรู้ Java!" อยู่ในกลุ่มสตริง แน่นอน มันคือ เราสร้างมันขึ้นมาด้วย

String s1 = "CodeGym is the best website for learning Java!";
เราตรวจสอบว่าs1และการอ้างอิงที่ส่งคืนโดยs2.intern()ชี้ไปยังพื้นที่หน่วยความจำเดียวกันหรือไม่ และแน่นอน พวกเขาทำ :) โดยสรุป จำและใช้กฎสำคัญนี้: เสมอใช้เท่ากับ()วิธีการเปรียบเทียบสตริง! เมื่อเปรียบเทียบสตริง เรามักหมายถึงการเปรียบเทียบอักขระมากกว่าการอ้างอิง พื้นที่ของหน่วยความจำ หรือสิ่งอื่นใด วิธี การ เท่ากับ ()ทำในสิ่งที่คุณต้องการ เพื่อเสริมสิ่งที่คุณได้เรียนรู้ เราขอแนะนำให้คุณดูบทเรียนวิดีโอจากหลักสูตร Java ของเรา
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION