CodeGym /Java блог /Случаен /Сравнете сравненията на String и Equals в Java
John Squirrels
Ниво
San Francisco

Сравнете сравненията на String и Equals в Java

Публикувано в групата
здрасти Днес ще говорим за една много важна и интересна тема, а именно сравняване на обекти с обекти (Compare Strings and Equals). Така че в Java, кога точно обект A ще бъде equals на обект 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 полета с еднакви стойности, но сравняването им пак ще даде false. В края на краищата препратки car1 и car2сочат към два различни обекта, т.е. към два различни address. Представете си ситуация, в която сравнявате хора. Със сигурност някъде по света има човек, който споделя вашето име, цвят на очите, възраст, ръст, цвят на косата и т.н. Това ви прави сходни в много отношения, но все пак не сте близнаци — и очевидно не сте едно и също лице.
Равенства и сравнения на низове - 2
Операторът == използва приблизително същата логика, когато го използваме за сравняване на два обекта. Но Howво ще стане, ако имате нужда вашата програма да използва различна логика? Да предположим например, че вашата програма извършва ДНК анализ. Той сравнява генетичния code на двама души и определя дали те са близнаци.

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 Получаваме същия логически резултат (защото не сме променor много), но сега тази логика не е добра! В крайна сметка, в реалния живот, ДНК анализът трябва да ни даде 100% гаранция, че имаме близнаци, които стоят пред нас. Но нашата програма и операторът == ни казват обратното. Как да променим това поведение и да се уверим, че програмата извежда правилния резултат, когато ДНК съвпада? 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));
   }
}
Конзолен изход: false Абсолютно същия резултат, така че за Howво ни трябва този метод? :/ Всичко е просто. Проблемът тук е, че в момента използваме този метод, Howто е имплементиран в класа Object . И ако влезем в codeа на класа Object и погледнем реализацията на метода, ето Howво ще видим:

public boolean equals(Object obj) {
   return (this == obj);
}
Това е причината поведението на програмата да не се е променило! Същият оператор == (който сравнява препратките) се използва в метода equals() на класа Object . Но трикът с този метод е, че можем да го отменим. Да замените означава да напишете свой собствен метод equals() в нашия клас Man , давайки му поведението, от което се нуждаем! В момента не ни харесва фактът, че man1.equals(man2) по същество е еквивалентен на man1 == man2 . Ето Howво ще направим в тази ситуация:

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 Сега получаваме съвсем различен резултат! Като написахме нашия собствен метод equals() и го използвахме instead of стандартния, създадохме правилното поведение: Сега, ако двама души имат една и съща ДНК, програмата съобщава „ДНК анализът е доказал, че са близнаци“ и връща true! Като замените метода equals() във вашите класове, можете лесно да създадете Howвато и да е логика за сравнение на обекти, от която се нуждаете. Всъщност току-що засегнахме сравнението на обекти. Пред нас все още има голям самостоятелен урок по тази тема (разгледайте го сега, ако ви е интересно).

Сравняване на низове в 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? В края на краищата, низовете са абсолютно еднакви, дума по дума :/ Може би сте се досетor за причината: това е, защото операторът == сравнява препратките ! Ясно е, че s1 и s2 имат различни addressи в паметта. Ако сте се сетor за това, нека преработим нашия пример:

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);
   }
}
Сега отново имаме две препратки, но резултатът е точно обратният: Конзолен изход: вярно Безпомощно объркан? Нека да разберем Howво става. Операторът == наистина сравнява addressите на паметта. Това винаги е вярно и не е нужно да се съмнявате в това. Това означава, че ако s1 == s2 върне true, тогава тези два низа имат един и същ address. И това наистина е вярно! Време е да ви представим специална област от паметта за съхранение на низове: пулът от низове
Равенства и сравнения на низове - 3
Пулът от низове е област за съхраняване на всички низови стойности, които създавате във вашата програма. Защо е създаден? Както казахме преди, низовете представляват огромен процент от всички обекти. Всяка голяма програма създава много низове. Пулът от низове е създаден, за да пести памет: низовете се поставят там и след това създадените впоследствие низове се отнасят към същата област от паметта - няма нужда да се разпределя допълнителна памет всеки път. Всеки път, когато пишете String = "........" програмата проверява дали има идентичен низ в пула от низове. Ако има, тогава няма да бъде създаден нов низ. И новата препратка ще сочи към същия address в набора от низове (където се намира идентичният низ). И така, когато писахме

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 се използва за създаване на обект, той принудително разпределя нова област от паметта за обекта. И низ, създаден с помощта на new , не завършва в пула от низове — той става отделен обект, дори ако неговият текст съвпада напълно с низ в пула от низове. Тоест, ако напишем следния code:

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 , се разпределя нова област от паметта, дори ако текстът в новия низ е същият! Изглежда сме разбрали оператора == . Но Howво да кажем за нашия нов познат, метода 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() може да бъде заменен, за да сравняваме обекти, Howто искаме? Точно това са направor с класа String . Той замества equals()метод. И instead of да сравнява препратките, той сравнява последователността от символи в низовете. Ако текстът е един и същ, тогава няма meaning How са създадени or къде се съхраняват: дали в пула от низове or отделна област от паметта. Резултатът от сравнението ще бъде верен. Между другото, Java ви позволява да извършвате сравнения на низове без големи и малки букви. Обикновено, ако един от низовете съдържа само главни букви, тогава резултатът от сравнението ще бъде false:

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() . Можете да го използвате, ако се интересувате само от сравняването на последователността от конкретни знаци, а не от главните букви. Например, това може да бъде полезно при сравняване на два address:

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));
   }
}
В този случай очевидно говорим за един и същ address, така че има смисъл да използвате метода equalsIgnoreCase() .

Методът String.intern().

Класът String има още един труден метод: intern() ; Методът intern() работи директно с пула от низове. Ако извикате метода intern() на няHowъв низ:
  • Той проверява дали има съвпадащ низ в набора от низове
  • Ако има, той връща препратката към низа в пула
  • Ако не, той добавя низа към набора от низове и връща препратка към него.
След като използваме метода 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() , резултатът беше false. Сега методът intern() проверява дали низът "CodeGym е най-добрият сайт за изучаване на Java!" е в пула от низове. Разбира се, така е: създадохме го с

String s1 = "CodeGym is the best website for learning Java!";
Проверяваме дали s1 и препратката, върната от s2.intern() , сочат към една и съща област от паметта. И разбира се, го правят :) В обобщение, запомнете и прилагайте това важно правило: ВИНАГИ използвайте метода equals() за сравняване на низове! Когато сравняваме низове, ние почти винаги имаме предвид да сравняваме техните знаци, а не препратки, области от паметта or нещо друго. Методът equals() прави точно това, от което се нуждаете. За да затвърдите наученото, ви предлагаме да гледате видео урок от нашия курс по Java

Още четене:

Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION