JavaRush /مدونة جافا /Random-AR /يساوي في جافا وسلسلة مقارنة - مقارنة السلسلة

يساوي في جافا وسلسلة مقارنة - مقارنة السلسلة

نشرت في المجموعة
مرحبًا! سنتحدث اليوم عن موضوع مهم ومثير للاهتمام للغاية وهو مقارنة الكائنات مع بعضها البعض يساوي () في Java. وبالفعل، في أي الحالات في Java سيكون الكائن 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تشير الروابط 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
من المنطقي أن النتيجة كانت نفسها (فبعد كل شيء، لم نغير أي شيء)، لكننا الآن لسنا سعداء بها! في الواقع، في الحياة الواقعية، يعد تحليل الحمض النووي ضمانًا بنسبة مائة بالمائة بأننا نواجه توأمان. لكن برنامجنا ومشغلنا ==يخبراننا بخلاف ذلك. فكيف يمكننا تغيير هذا السلوك والتأكد من أنه في حالة تطابق اختبارات الحمض النووي فإن البرنامج سيعطي النتيجة الصحيحة؟ لهذا الغرض، تم إنشاء طريقة خاصة في Java -equals () .

يساوي () الأسلوب في جافا

مثل الطريقة 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
نتيجة مختلفة تماما! من خلال كتابة طريقة يساوي () الخاصة بنا بدلاً من الطريقة القياسية، حققنا السلوك الصحيح: الآن إذا كان لدى شخصين نفس رمز الحمض النووي، يخبرنا البرنامج: "أظهر تحليل الحمض النووي أنهما توأمان" ويعود صحيحًا! من خلال تجاوز طريقة يساوي () في فئاتك، يمكنك بسهولة إنشاء منطق مقارنة الكائنات الضروري. لقد تطرقنا إلى مقارنة الأشياء بعبارات عامة فقط. لا يزال أمامنا محاضرة كبيرة منفصلة حول هذا الموضوع (يمكنك قراءتها بسرعة الآن، إذا كنت مهتمًا).

مقارنة السلسلة في 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