CodeGym /בלוג Java /Random-HE /השווה השוואות מחרוזות ושווה ב-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);
   }
}
פלט מסוף: false המתן, עצור. למה זה ששתי המכוניות האלה לא שוות? הקצינו להם את אותם מאפיינים, אבל תוצאת ההשוואה שקרית. התשובה פשוטה. האופרטור == משווה הפניות לאובייקט, לא מאפייני אובייקט. לשני אובייקטים יכולים להיות אפילו 500 שדות עם ערכים זהים, אבל השוואה ביניהם עדיין תניב שקר. אחרי הכל, הפניות car1 ו- car2 מצביעות על שני אובייקטים שונים, כלומר על שתי כתובות שונות. תאר לעצמך מצב שבו אתה משווה בין אנשים. אין ספק, איפשהו בעולם יש אדם שחולק את אותו השם שלך, צבע עיניים, גיל, גובה, צבע שיער וכו'. זה גורם לך להיות דומים במובנים רבים, אבל אתה עדיין לא תאומים - ואתה כמובן לא אותו אדם.
שווים והשוואות מחרוזות - 2
האופרטור == משתמש בערך באותו היגיון כאשר אנו משתמשים בו כדי להשוות בין שני אובייקטים. אבל מה אם אתה צריך שהתוכנית שלך תשתמש בלוגיקה אחרת? לדוגמה, נניח שהתוכנית שלך מבצעת ניתוח DNA. הוא משווה את הקוד הגנטי של שני אנשים, וקובע אם הם תאומים.
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);
   }
}
פלט מסוף: false אנחנו מקבלים את אותה תוצאה הגיונית (כי לא שינינו הרבה), אבל עכשיו ההיגיון הזה לא טוב! אחרי הכל, בחיים האמיתיים, ניתוח DNA אמור לתת לנו ערובה של 100% שיש לנו תאומים שעומדים מולנו. אבל התוכנית שלנו והמפעיל == אומרים לנו את ההיפך. כיצד אנו משנים התנהגות זו ומוודאים שהתוכנית תוציא את התוצאה הנכונה כאשר ה-DNA תואם? ל-Java יש שיטה מיוחדת לכך: equals() . בדומה לשיטת toString() שדיברנו עליה קודם, equals() שייכת למחלקה Object - המחלקה החשובה ביותר ב-Java, המחלקה ממנה נובעות כל המחלקות האחרות. אבל equals() לא משנה את התנהגות התוכנית שלנו לבד:
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));
   }
}
פלט מסוף: false בדיוק אותה תוצאה, אז בשביל מה אנחנו צריכים את השיטה הזו? :/ הכל פשוט. הבעיה כאן היא שאנו משתמשים כעת בשיטה זו כפי שהיא מיושמת במחלקה Object . ואם ניכנס לקוד של המחלקה Object ונסתכל על היישום של השיטה, זה מה שנראה:
public boolean equals(Object obj) {
   return (this == obj);
}
זו הסיבה שהתנהגות התוכנית לא השתנתה! אותו אופרטור == ממש (שמשווה הפניות) משמש בתוך שיטת equals() של מחלקה Object . אבל החוכמה בשיטה הזו היא שאנחנו יכולים לעקוף אותה. לעקוף פירושו לכתוב את שיטת equals() משלך במחלקה 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));

   }
}
פלט מסוף: נכון עכשיו אנחנו מקבלים תוצאה אחרת לגמרי! על ידי כתיבת שיטת equals() משלנו ושימוש בה במקום בשיטת הסטנדרטית, יצרנו את ההתנהגות הנכונה: עכשיו אם לשני אנשים יש אותו DNA, התוכנית מדווחת "ניתוח DNA הוכיח שהם תאומים" ומחזירה אמת! על ידי דריסת שיטת equals() במחלקות שלך, אתה יכול ליצור בקלות כל לוגיקה להשוואת אובייקטים שאתה צריך. למעשה, רק עכשיו נגענו בהשוואת אובייקטים. לפנינו, עדיין יש שיעור עצמאי גדול בנושא זה (אתה גולש עליו עכשיו אם אתה מעוניין).

השוואת מחרוזות ב-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);
   }
}
פלט מסוף: false אבל למה קיבלנו false? אחרי הכל, המחרוזות זהות לחלוטין, מילה במילה :/ אולי ניחשתם את הסיבה: זה בגלל שהאופרטור == משווה הפניות ! ברור של- 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 , אזור חדש של זיכרון מוקצה, גם אם הטקסט בתוך המחרוזת החדשה זהה! נראה שמצאנו את האופרטור == . אבל מה לגבי ההיכרות החדשה שלנו, שיטת equals() ?
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 מצביעים על אזורים שונים בזיכרון. אבל השיטה equals() עדיין אומרת לנו שהם שווים. למה? זוכרים שאמרנו בעבר שאפשר לעקוף את שיטת equals() כדי להשוות אובייקטים איך שאנחנו רוצים? זה בדיוק מה שהם עשו עם הכיתה String . זה עוקף את שיטת equals() . ובמקום להשוות הפניות, הוא משווה את רצף התווים במחרוזות. אם הטקסט זהה, אז זה לא משנה איך הם נוצרו או איפה הם מאוחסנים: אם במאגר המיתרים או באזור נפרד של זיכרון. תוצאת ההשוואה תהיה נכונה. דרך אגב, 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));
   }
}
פלט מסוף: false עבור השוואות לא תלויות רישיות, למחלקה String יש את השיטה 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() פועלת ישירות עם מאגר המחרוזות. אם אתה קורא לשיטת 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());
   }
}
פלט מסוף: true כאשר השוו את המחרוזות הללו בעבר ללא intern() , התוצאה הייתה שקר. כעת שיטת intern() בודקת אם המחרוזת "CodeGym הוא האתר הטוב ביותר ללימוד Java!" נמצא בבריכת המיתרים. כמובן, זהו: יצרנו את זה עם
String s1 = "CodeGym is the best website for learning Java!";
אנו בודקים אם ה- s1 וההפניה המוחזרת על ידי s2.intern() מצביעות על אותו אזור זיכרון. וכמובן, הם עושים זאת :) לסיכום, שנן ויישם את הכלל החשוב הזה: השתמש תמיד בשיטת equals() כדי להשוות מחרוזות! כאשר משווים מחרוזות, אנו מתכוונים כמעט תמיד להשוות את הדמויות שלהם ולא להפניות, אזורי זיכרון או כל דבר אחר. השיטה equals() עושה בדיוק את מה שאתה צריך. כדי לחזק את מה שלמדת, אנו מציעים לך לצפות בשיעור וידאו מקורס Java שלנו

קריאה נוספת:

הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION