JavaRush /وبلاگ جاوا /Random-FA /برابر است در جاوا و مقایسه رشته ها - مقایسه رشته ها

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

در گروه منتشر شد
سلام! امروز در مورد یک موضوع بسیار مهم و جالب صحبت خواهیم کرد، یعنی مقایسه اشیا با یکدیگر ()quals() در جاوا. و به راستی در چه مواردی در جاوا شی A برابر با شی 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اشاره می کنند . موقعیتی را با مقایسه افراد تصور کنید. احتمالاً فردی در دنیا وجود دارد که نام، رنگ چشم، سن، قد، رنگ مو و غیره با شما یکی باشد. یعنی شما از بسیاری جهات شبیه هم هستید، اما باز هم دوقلو نیستید و به خصوص یک فرد مشابه نیستید. عملگر تقریباً همان منطق را اعمال می کند که از آن برای مقایسه دو شی استفاده می کنیم. اما اگر به منطق متفاوتی در برنامه خود نیاز دارید چه؟ به عنوان مثال، اگر برنامه شما آنالیز DNA را شبیه سازی کند. او باید کد DNA دو نفر را مقایسه کند و تشخیص دهد که آنها دوقلو هستند. car2 برابر و مقایسه رشته ها - 2==
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 مطابقت داشته باشند، برنامه نتیجه درستی را ایجاد می‌کند؟ برای این منظور، یک متد خاص در جاوا ایجاد شد -quals() .

متد Equals() در جاوا

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

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

چرا ما با مقایسه رشته ها جدا از هر چیز دیگری رفتار می کنیم؟ خوب، در واقع، خطوط در برنامه نویسی داستان کاملاً متفاوتی هستند. اولاً، اگر تمام برنامه های جاوا را که توسط بشر نوشته شده است را انتخاب کنید، حدود 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 == s2true را برگرداند، این دو رشته آدرس یکسانی در حافظه دارند. و در واقع همینطور است! وقت آن است که با یک منطقه حافظه ویژه برای ذخیره رشته ها آشنا شوید - استخر رشته ها ( String pool) برابر و مقایسه رشته ها - 3String 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. دستور اول یک خط جدید در استرینگ pool با متن مورد نیاز ما ایجاد کرد و وقتی نوبت به دومی رسید، به سادگی به همان ناحیه حافظه اشاره کرد 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یک ناحیه جدید به حافظه اختصاص می یابد، حتی اگر متن داخل خطوط جدید یکسان باشد! به نظر می رسد که ما عملگر را مرتب کرده ایم ==، اما دوست جدیدمان - متد ()quals چطور؟
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به مناطق مختلف در حافظه اشاره می کنیم. اما، با این وجود، متد ()quals می گوید که آنها برابر هستند. چرا؟ به یاد داشته باشید، در بالا گفتیم که متد ()quals را می توان در کلاس شما بازنویسی کرد تا اشیا را به روشی که شما نیاز دارید مقایسه کند؟ این کاری است که آنها با کلاس انجام دادند String. دارای یک متد برابر ()برابر شده است. و نه پیوندها، بلکه توالی کاراکترها را در رشته ها مقایسه می کند. و اگر متن در رشته ها یکسان باشد، مهم نیست که چگونه ایجاد شده اند و در کجا ذخیره می شوند: در مجموعه رشته ها یا در یک منطقه حافظه جداگانه. نتیجه مقایسه درست خواهد بود. به هر حال، جاوا به شما این امکان را می دهد که رشته ها را به روشی بدون حروف بزرگ و کوچک مقایسه کنید. در یک وضعیت عادی، اگر یکی از خطوط را مثلاً با حروف بزرگ بنویسید، نتیجه مقایسه نادرست خواهد بود:
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 - بهترین سایت برای یادگیری جاوا!" در استخر رشته البته وجود دارد: زمانی که نوشتیم آن را ایجاد کردیم
String s1 = "JavaRush is the best site to learn Java!";
بررسی شد که مرجع s1و مرجع برگردانده شده توسط متد s2.intern()به یک منطقه در حافظه اشاره می کنند و البته این کار را انجام می دهند :) برای خلاصه کردن، به خاطر بسپارید و از قانون اصلی استفاده کنید: برای مقایسه رشته ها، همیشه از برابر () استفاده کنید () روش! هنگام مقایسه رشته ها، تقریباً همیشه منظور شما مقایسه متن آنهاست، نه پیوندها، مناطق حافظه و غیره. متد ()quals دقیقاً همان کاری را انجام می دهد که شما نیاز دارید. در اینجا چند لینک برای شما وجود دارد که می توانید خودتان مطالعه کنید:
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION