CodeGym /Java Blog /Willekeurig /Vergelijk String- en Equals-vergelijkingen in Java
John Squirrels
Niveau 41
San Francisco

Vergelijk String- en Equals-vergelijkingen in Java

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag zullen we het hebben over een heel belangrijk en interessant onderwerp, namelijk het vergelijken van objecten met objecten (Vergelijk strings en is gelijk aan). Dus in Java, wanneer zou object A precies gelijk zijn aan object B ? Laten we proberen een voorbeeld te schrijven:

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);
   }
}
Console-uitvoer: false Wacht, stop. Hoe komt het dat deze twee auto's niet gelijk zijn? We hebben ze dezelfde eigenschappen toegewezen, maar het resultaat van de vergelijking is onjuist. Het antwoord is simpel. De operator == vergelijkt objectreferenties, niet objecteigenschappen. Twee objecten kunnen zelfs 500 velden met identieke waarden hebben, maar als u ze vergelijkt, levert dat nog steeds false op. Verwijst immers naar auto1 en auto2wijzen naar twee verschillende objecten, dwz naar twee verschillende adressen. Stel je een situatie voor waarin je mensen vergelijkt. Zeker, ergens in de wereld is er een persoon die dezelfde naam, oogkleur, leeftijd, lengte, haarkleur, enz. deelt. Dat maakt dat je in veel opzichten op elkaar lijkt, maar je bent nog steeds geen tweeling - en dat ben je duidelijk ook niet dezelfde persoon.
Gelijken en stringvergelijkingen - 2
De operator == gebruikt ongeveer dezelfde logica als we deze gebruiken om twee objecten te vergelijken. Maar wat als u wilt dat uw programma andere logica gebruikt? Stel dat uw programma een DNA-analyse uitvoert. Het vergelijkt de genetische code van twee mensen en bepaalt of ze een tweeling zijn.

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);
   }
}
Console output: false We krijgen hetzelfde logische resultaat (omdat we niet veel veranderd hebben), maar nu deugt die logica niet meer! In het echte leven zou DNA-analyse ons immers 100% garantie moeten geven dat we een tweeling voor ons hebben staan. Maar ons programma en de == operator vertellen ons het tegenovergestelde. Hoe kunnen we dit gedrag veranderen en ervoor zorgen dat het programma het juiste resultaat geeft wanneer het DNA overeenkomt? Java heeft hiervoor een speciale methode: equals() . Net als de methode toString() die we eerder hebben besproken, behoort equals() tot de klasse Object — de belangrijkste klasse in Java, de klasse waarvan alle andere klassen zijn afgeleid. Maar is gelijk aan()verandert het gedrag van ons programma niet vanzelf:

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));
   }
}
Console-uitvoer: false Precies hetzelfde resultaat, dus waar hebben we deze methode voor nodig? :/ Het is allemaal simpel. Het probleem hier is dat we deze methode momenteel gebruiken omdat deze is geïmplementeerd in de klasse Object . En als we de code van de klasse Object ingaan en kijken naar de implementatie van de methode, dan is dit wat we zullen zien:

public boolean equals(Object obj) {
   return (this == obj);
}
Dat is de reden waarom het gedrag van het programma niet is veranderd! Dezelfde operator == (die referenties vergelijkt) wordt gebruikt in de methode equals() van de klasse Object . Maar de truc met deze methode is dat we het kunnen overschrijven. Overschrijven betekent dat u uw eigen methode equals() in onze klasse Man schrijft en deze het gedrag geeft dat we nodig hebben! Op dit moment houden we niet van het feit dat man1.equals(man2) in wezen gelijk is aan man1 == man2 . Dit is wat we in deze situatie zullen doen:

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)); 

   } 
} 
Console-uitvoer: waar Nu krijgen we een heel ander resultaat! Door onze eigen methode equals() te schrijven en deze te gebruiken in plaats van de standaardmethode, hebben we het juiste gedrag geproduceerd: als twee mensen hetzelfde DNA hebben, rapporteert het programma "DNA-analyse heeft bewezen dat ze een tweeling zijn" en retourneert true! Door de methode equals() in uw klassen te overschrijven, kunt u eenvoudig elke objectvergelijkingslogica maken die u nodig hebt. In feite hebben we het nu nog maar net gehad over objectvergelijking. Voor ons ligt er nog een grote op zichzelf staande les over dit onderwerp (je bladert er nu even doorheen als je geïnteresseerd bent).

Strings vergelijken in Java

Waarom beschouwen we stringvergelijkingen los van al het andere? De realiteit is dat strings een op zichzelf staand onderwerp zijn bij het programmeren. Ten eerste, als je alle Java-programma's neemt die ooit zijn geschreven, zul je zien dat ongeveer 25% van de objecten daarin strings zijn. Dit onderwerp is dus erg belangrijk. Ten tweede is het proces van het vergelijken van strings heel anders dan bij andere objecten. Overweeg een eenvoudig voorbeeld:

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);
   }
}
Console-uitvoer: false Maar waarom kregen we false? De strings zijn tenslotte exact hetzelfde, woord voor woord :/ Je raadt de reden misschien al: het is omdat de == operator referenties vergelijkt ! Het is duidelijk dat s1 en s2 verschillende adressen in het geheugen hebben. Als je daaraan hebt gedacht, laten we dan ons voorbeeld herwerken:

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);
   }
}
Nu hebben we weer twee referenties, maar het resultaat is precies het tegenovergestelde: Console-uitvoer: waar Hulpeloos verward? Laten we uitzoeken wat er aan de hand is. De operator == vergelijkt echt geheugenadressen. Dit is altijd waar en u hoeft er niet aan te twijfelen. Dat betekent dat als s1 == s2 waar retourneert, deze twee strings hetzelfde adres hebben. En inderdaad dit is waar! Het is tijd om u kennis te laten maken met een speciaal geheugengebied voor het opslaan van strings: de stringpool
Gelijken en stringvergelijkingen - 3
De stringpool is een gebied voor het opslaan van alle stringwaarden die u in uw programma maakt. Waarom is het gemaakt? Zoals we al eerder zeiden, vertegenwoordigen strings een enorm percentage van alle objecten. Elk groot programma creëert veel strings. De stringpool is gemaakt om geheugen te besparen: strings worden daar geplaatst en vervolgens verwijzen strings naar hetzelfde geheugengebied - het is niet nodig om elke keer extra geheugen toe te wijzen. Elke keer dat je String = "........." schrijft, controleert het programma of er een identieke string in de stringpool zit. Als dat zo is, wordt er geen nieuwe string gemaakt. En de nieuwe referentie verwijst naar hetzelfde adres in de stringpool (waar de identieke string zich bevindt). Dus toen we schreven

String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2 wijst naar dezelfde plaats als s1 . De eerste instructie maakt een nieuwe string aan in de stringpool. De tweede verklaring verwijst simpelweg naar hetzelfde geheugengebied als s1 . Je zou nog eens 500 identieke strings kunnen maken en het resultaat zou niet veranderen. Wacht even. Als dat waar is, waarom werkte dit voorbeeld dan niet eerder?

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);
   }
}
Ik denk dat je intuïtie je de reden al heeft verteld =) Probeer te raden voordat je verder leest. U kunt zien dat deze twee strings op verschillende manieren zijn gedeclareerd. Een met de nieuwe operator en de andere zonder. Hierin ligt de reden. Wanneer de operator new wordt gebruikt om een ​​object te maken, wordt er krachtig een nieuw geheugengebied voor het object toegewezen. En een string die met new is gemaakt , komt niet in de stringpool terecht — het wordt een apart object, zelfs als de tekst ervan perfect overeenkomt met een string in de stringpool. Dat wil zeggen, als we de volgende code schrijven:

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!");
   }
}
Ter herinnering ziet het er zo uit:
Gelijken en stringvergelijkingen - 4
En elke keer dat u een nieuw object maakt met behulp van new , wordt een nieuw geheugengebied toegewezen, zelfs als de tekst in de nieuwe string hetzelfde is! Het lijkt erop dat we de == operator hebben ontdekt . Maar hoe zit het met onze nieuwe kennis, de methode 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));
   }
}
Console-uitvoer: waar Interessant. We zijn er zeker van dat s1 en s2 naar verschillende geheugengebieden wijzen. Maar de methode equals() vertelt ons nog steeds dat ze gelijk zijn. Waarom? Weet je nog dat we eerder zeiden dat de methode equals() kan worden overschreven om objecten te vergelijken zoals we dat willen? Dat is precies wat ze hebben gedaan met de klasse String . Het overschrijft de equals()methode. En in plaats van verwijzingen te vergelijken, vergelijkt het de reeks tekens in de tekenreeksen. Als de tekst hetzelfde is, maakt het niet uit hoe ze zijn gemaakt of waar ze zijn opgeslagen: in de stringpool of in een apart geheugengebied. Het resultaat van de vergelijking zal waar zijn. Overigens kunt u met Java hoofdletterongevoelige tekenreeksvergelijkingen uitvoeren. Normaal gesproken, als een van de strings allemaal hoofdletters heeft, is het resultaat van de vergelijking onwaar:

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));
   }
}
Console-uitvoer: false Voor hoofdletterongevoelige vergelijkingen heeft de klasse String de methode equalsIgnoreCase() . U kunt het gebruiken als u alleen de volgorde van specifieke tekens wilt vergelijken in plaats van hoofdlettergebruik. Dit kan bijvoorbeeld handig zijn bij het vergelijken van twee adressen:

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));
   }
}
In dit geval hebben we het duidelijk over hetzelfde adres, dus is het logisch om de methode equalsIgnoreCase() te gebruiken .

De String.intern() methode

De klasse String heeft nog een lastigere methode: intern() ; De methode intern() werkt rechtstreeks met de stringpool. Als je de methode intern() aanroept op een string:
  • Het controleert of er een overeenkomende string in de stringpool is
  • Als dat zo is, wordt de verwijzing naar de tekenreeks in de pool geretourneerd
  • Zo niet, dan wordt de string aan de stringpool toegevoegd en wordt er een verwijzing naar geretourneerd.
Na het gebruik van de methode intern() op een stringreferentie verkregen met new , kunnen we de == operator gebruiken om deze te vergelijken met een stringreferentie uit de stringpool.

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());
   }
}
Console-uitvoer: true Toen we deze strings eerder vergeleken zonder intern() , was het resultaat false. Nu controleert de methode intern() of de string "CodeGym is de beste site om Java te leren!" zit in de stringpool. Natuurlijk is het: we hebben het gemaakt met

String s1 = "CodeGym is the best website for learning Java!";
We controleren of de s1 en de referentie geretourneerd door s2.intern() naar hetzelfde geheugengebied verwijzen. En natuurlijk doen ze dat :) Kortom, onthoud deze belangrijke regel en pas deze toe: gebruik ALTIJD de methode equals() om strings te vergelijken! Bij het vergelijken van tekenreeksen bedoelen we bijna altijd dat we hun karakters vergelijken in plaats van referenties, geheugengebieden of iets anders. De methode equals() doet precies wat u nodig hebt. Om te versterken wat je hebt geleerd, raden we je aan een videoles van onze Java-cursus te bekijken
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION