CodeGym /وبلاگ جاوا /Random-FA /مقایسه رشته ها و مقایسه های برابر در جاوا
John Squirrels
مرحله
San Francisco

مقایسه رشته ها و مقایسه های برابر در جاوا

در گروه منتشر شد
سلام! امروز در مورد یک موضوع بسیار مهم و جالب صحبت خواهیم کرد، یعنی مقایسه اشیا با اشیا (مقایسه رشته ها و برابرها). بنابراین در جاوا، دقیقاً چه زمانی شی 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 با هم مطابقت دارد، نتیجه صحیح را ارائه می دهد؟ جاوا یک متد خاص برای این کار دارد: equals() . مانند متد toString() که قبلاً در مورد آن صحبت کردیم، ()quals متعلق به کلاس Object است - مهمترین کلاس در جاوا، کلاسی که همه کلاس های دیگر از آن مشتق می شوند. اما ()quals رفتار برنامه ما را به خودی خود تغییر نمی دهد:
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);
}
به همین دلیل رفتار برنامه تغییر نکرده است! همان عملگر == (که مراجع را با هم مقایسه می کند) در متد () quals کلاس Object استفاده می شود . اما ترفند این روش این است که می توانیم آن را نادیده بگیریم. Override به این معنی است که متد برابری () خود را در کلاس 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 حالا نتیجه کاملا متفاوتی دریافت می کنیم! با نوشتن متد () quals خودمان و استفاده از آن به جای استاندارد، رفتار درستی را ایجاد کرده‌ایم: حالا اگر دو نفر DNA یکسانی داشته باشند، برنامه گزارش می‌دهد "تحلیل DNA ثابت کرده است که دوقلو هستند" و true را برمی‌گرداند! با نادیده گرفتن متد () quals در کلاس های خود، می توانید به راحتی هر منطق مقایسه شی مورد نیاز خود را ایجاد کنید. در واقع، ما فقط به مقایسه شیء پرداختیم. پیش روی ما، هنوز یک درس مستقل بزرگ در مورد این موضوع وجود دارد (اگر علاقه دارید، اکنون آن را مرور کنید).

مقایسه رشته ها در جاوا

چرا مقایسه رشته ها را جدا از هر چیز دیگری در نظر می گیریم؟ واقعیت این است که رشته ها به تنهایی یک موضوع در برنامه نویسی هستند. ابتدا، اگر تمام برنامه‌های جاوا را که تا به حال نوشته شده‌اند را انتخاب کنید، متوجه می‌شوید که حدود 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 Pool ناحیه ای برای ذخیره تمام مقادیر رشته ای است که در برنامه خود ایجاد می کنید. چرا ایجاد شد؟ همانطور که قبلاً گفتیم، رشته ها درصد زیادی از تمام اشیاء را نشان می دهند. هر برنامه بزرگی رشته های زیادی ایجاد می کند. مجموعه رشته ها برای ذخیره حافظه ایجاد شده است: رشته ها در آنجا قرار می گیرند و سپس رشته های ایجاد شده به همان ناحیه از حافظه اشاره می کنند - نیازی به تخصیص حافظه اضافی در هر بار وجود ندارد. هر بار که 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 ایجاد می کنید ، یک ناحیه جدید از حافظه اختصاص داده می شود، حتی اگر متن داخل رشته جدید یکسان باشد! به نظر می رسد که ما عملگر == را کشف کرده ایم . اما در مورد آشنای جدید ما، متد () quals چطور؟
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 به مناطق مختلفی در حافظه اشاره می کنند. اما متد () quals همچنان به ما می گوید که آنها برابر هستند. چرا؟ به یاد داشته باشید که قبلاً گفتیم که متد () quals می‌تواند برای مقایسه اشیاء هر طور که می‌خواهیم لغو شود؟ این همان کاری است که آنها با کلاس String انجام داده اند . متد () quals را لغو می کند . و به جای مقایسه مراجع، توالی کاراکترها را در رشته ها مقایسه می کند. اگر متن یکسان است، مهم نیست که چگونه ایجاد شده اند یا در کجا ذخیره می شوند: چه در مجموعه رشته ها یا یک منطقه جداگانه از حافظه. نتیجه مقایسه درست خواهد بود. به هر حال، جاوا به شما این امکان را می‌دهد که مقایسه رشته‌ها به حروف بزرگ و کوچک را انجام دهید. به طور معمول، اگر یکی از رشته ها دارای تمام حروف بزرگ باشد، نتیجه مقایسه نادرست خواهد بود:
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 دارای متد ()qualsIgnoreCase است . اگر فقط به مقایسه توالی کاراکترهای خاص اهمیت می دهید تا حروف بزرگ، می توانید از آن استفاده کنید. به عنوان مثال، این می تواند هنگام مقایسه دو آدرس مفید باشد:
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));
   }
}
در این مورد، واضح است که ما در مورد یک آدرس صحبت می کنیم، بنابراین منطقی است که از متد ()qualsIgnoreCase استفاده کنیم .

متد String.intern().

کلاس String یک روش پیچیده دیگر نیز دارد: intern() ; متد intern() مستقیماً با string pool کار می کند. اگر متد 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 بهترین سایت برای یادگیری جاوا است!" در استخر رشته است. البته این است: ما آن را با
String s1 = "CodeGym is the best website for learning Java!";
بررسی می کنیم که آیا s1 و مرجع بازگشتی توسط s2.intern() به یک ناحیه از حافظه اشاره می کنند یا خیر. و البته این کار را می کنند :) به طور خلاصه، این قانون مهم را به خاطر بسپارید و اعمال کنید: همیشه از متد () quals برای مقایسه رشته ها استفاده کنید! هنگام مقایسه رشته ها، تقریباً همیشه منظور ما مقایسه کاراکترهای آنها به جای ارجاعات، حوزه های حافظه یا هر چیز دیگری است. متد () quals دقیقاً همان کاری را انجام می دهد که شما نیاز دارید. برای تقویت آموخته هایتان، پیشنهاد می کنیم یک درس ویدیویی از دوره جاوا ما تماشا کنید

ادامه مطلب:

نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION