CodeGym /Java blogg /Slumpmässig /Jämför String och Equals jämförelser i Java
John Squirrels
Nivå
San Francisco

Jämför String och Equals jämförelser i Java

Publicerad i gruppen
Hej! Idag kommer vi att prata om ett mycket viktigt och intressant ämne, nämligen att jämföra objekt med objekt (Compare Strings and Equals). Så i Java, när exakt skulle objekt A vara lika med objekt B ? Låt oss försöka skriva ett exempel:

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);
   }
}
Konsolutgång: falsk Vänta, stopp. Varför är det så att dessa två bilar inte är lika? Vi tilldelade dem samma egenskaper, men resultatet av jämförelsen är falskt. Svaret är enkelt. Operatorn == jämför objektreferenser, inte objektegenskaper. Två objekt kan till och med ha 500 fält med identiska värden, men att jämföra dem skulle fortfarande ge falskt. När allt kommer omkring refererar bil1 och bil2peka på två olika objekt, dvs på två olika adresser. Föreställ dig en situation där du jämför människor. Visst, någonstans i världen finns det en person som delar ditt namn, ögonfärg, ålder, längd, hårfärg, etc. Det gör dig lika i många avseenden, men du är fortfarande inte tvillingar - och du är uppenbarligen inte samma person.
Lika och strängjämförelser - 2
Operatorn == använder ungefär samma logik när vi använder den för att jämföra två objekt. Men vad händer om du behöver ditt program för att använda annan logik? Anta till exempel att ditt program utför DNA-analys. Den jämför den genetiska koden för två personer och avgör om de är tvillingar.

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);
   }
}
Konsolutgång: false Vi får samma logiska resultat (eftersom vi inte ändrade mycket), men nu är den logiken inte bra! När allt kommer omkring, i det verkliga livet borde DNA-analys ge oss en 100% garanti för att vi har tvillingar som står framför oss. Men vårt program och ==- operatören säger oss motsatsen. Hur ändrar vi detta beteende och ser till att programmet ger rätt resultat när DNA matchar? Java har en speciell metod för detta: equals() . Liksom toString()- metoden, som vi diskuterade tidigare, tillhör equals() klassen Object — den viktigaste klassen i Java, klassen som alla andra klasser härrör från. Men lika()ändrar inte vårt programs beteende helt av sig själv:

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));
   }
}
Konsolutgång: falskt Exakt samma resultat, så vad behöver vi den här metoden till? :/ Allt är enkelt. Problemet här är att vi för närvarande använder den här metoden eftersom den är implementerad i klassen Object . Och om vi går in i koden för klassen Object och tittar på metodens implementering, är detta vad vi kommer att se:

public boolean equals(Object obj) {
   return (this == obj);
}
Det är anledningen till att programmets beteende inte har förändrats! Samma ==- operator (som jämför referenser) används i metoden equals() i klassen Object . Men tricket med den här metoden är att vi kan åsidosätta den. Att åsidosätta betyder att skriva din egen equals() -metod i vår Man -klass, vilket ger den det beteende vi behöver! För närvarande gillar vi inte det faktum att man1.equals(man2) i huvudsak är ekvivalent med man1 == man2 . Så här gör vi i den här situationen:

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

   } 
} 
Konsolutgång: sant Nu får vi ett helt annat resultat! Genom att skriva vår egen equals()- metod och använda den istället för standardmetoden, har vi tagit fram det korrekta beteendet: Om två personer har samma DNA, rapporterar programmet "DNA-analys har visat att de är tvillingar" och returnerar sant! Genom att åsidosätta metoden equals() i dina klasser kan du enkelt skapa vilken objektjämförelselogik du behöver. Faktum är att vi bara har berört objektjämförelse. Framför oss finns det fortfarande en stor fristående lektion om detta ämne (du skummar över den nu om du är intresserad).

Jämför strängar i Java

Varför överväger vi strängjämförelser separat från allt annat? Verkligheten är att strängar är ett eget ämne i programmering. För det första, om du tar alla Java-program som någonsin skrivits, kommer du att upptäcka att cirka 25 % av objekten i dem är strängar. Så det här ämnet är väldigt viktigt. För det andra är processen att jämföra strängar väldigt annorlunda än andra objekt. Tänk på ett enkelt exempel:

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);
   }
}
Konsolutgång: falsk Men varför fick vi falsk? När allt kommer omkring är strängarna exakt likadana, ord för ord :/ Du kanske har gissat anledningen: det är för att operatorn == jämför referenser ! Uppenbarligen har s1 och s2 olika adresser i minnet. Om du tänkte på det, låt oss omarbeta vårt exempel:

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 har vi återigen två referenser, men resultatet är raka motsatsen: Konsolutgång: sant Hjälplöst förvirrad? Låt oss ta reda på vad som händer. Operatören == jämför verkligen minnesadresser. Detta är alltid sant och du behöver inte tvivla på det. Det betyder att om s1 == s2 returnerar sant, så har dessa två strängar samma adress. Och det är verkligen sant! Det är dags att introducera dig till ett speciellt minnesområde för lagring av strängar: strängpoolen
Lika och strängjämförelser - 3
Strängpoolen är ett område för att lagra alla strängvärden du skapar i ditt program. Varför skapades den? Som vi sa tidigare representerar strängar en stor andel av alla objekt. Alla stora program skapar många strängar. Strängpoolen skapades för att spara minne: strängar placeras där och sedan skapade strängar refererar till samma minnesområde – det finns inget behov av att allokera ytterligare minne varje gång. Varje gång du skriver String = "........" kontrollerar programmet om det finns en identisk sträng i strängpoolen. Om det finns det skapas inte en ny sträng. Och den nya referensen kommer att peka på samma adress i strängpoolen (där den identiska strängen finns). Så när vi skrev

String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2 pekar på samma plats som s1 . Den första satsen skapar en ny sträng i strängpoolen. Den andra satsen hänvisar helt enkelt till samma minnesområde som s1 . Du kan göra ytterligare 500 identiska strängar och resultatet skulle inte förändras. Vänta en minut. Om det är sant, varför fungerade då inte det här exemplet tidigare?

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);
   }
}
Jag tror att din intuition redan har berättat orsaken =) Försök gissa innan du läser vidare. Du kan se att dessa två strängar deklarerades på olika sätt. Den ena med den nya operatören och den andra utan den. Häri ligger anledningen. När den nya operatorn används för att skapa ett objekt, allokerar den med kraft ett nytt minnesområde för objektet. Och en sträng skapad med nytt hamnar inte i strängpoolen – den blir ett separat objekt, även om dess text perfekt matchar en sträng i strängpoolen. Det vill säga om vi skriver följande kod:

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!");
   }
}
I minnet ser det ut så här:
Lika och strängjämförelser - 4
Och varje gång du skapar ett nytt objekt med new , tilldelas ett nytt minnesområde, även om texten inuti den nya strängen är densamma! Det verkar som om vi har listat ut operatorn == . Men hur är det med vår nya bekantskap, metoden 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));
   }
}
Konsolutgång: sant Intressant. Vi är säkra på att s1 och s2 pekar på olika områden i minnet. Men metoden equals() säger oss fortfarande att de är lika. Varför? Kom ihåg att vi tidigare sa att metoden equals() kan åsidosättas för att jämföra objekt hur vi vill? Det är precis vad de har gjort med String -klassen. Det åsidosätter lika()metod. Och istället för att jämföra referenser, jämför den sekvensen av tecken i strängarna. Om texten är densamma spelar det ingen roll hur de skapades eller var de lagras: i strängpoolen eller ett separat minnesområde. Resultatet av jämförelsen kommer att vara sant. Java låter dig förresten utföra skiftlägesokänsliga strängjämförelser. Normalt, om en av strängarna har alla versaler, blir resultatet av jämförelsen falskt:

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));
   }
}
Konsolutdata: false För skiftlägesokänsliga jämförelser har klassen String metoden equalsIgnoreCase() . Du kan använda det om du bara bryr dig om att jämföra sekvensen av specifika tecken snarare än skiftläge. Detta kan till exempel vara användbart när du jämför två adresser:

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));
   }
}
I det här fallet talar vi uppenbarligen om samma adress, så det är vettigt att använda metoden equalsIgnoreCase() .

Metoden String.intern()

Klassen String har ytterligare en knepig metod: intern() ; Intern () -metoden fungerar direkt med strängpoolen. Om du anropar intern() -metoden på någon sträng:
  • Den kontrollerar om det finns en matchande sträng i strängpoolen
  • Om det finns, returnerar den referensen till strängen i poolen
  • Om inte, lägger den till strängen i strängpoolen och returnerar en referens till den.
Efter att ha använt metoden intern() på en strängreferens som erhålls med new , kan vi använda operatorn == för att jämföra den med en strängreferens från strängpoolen.

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());
   }
}
Konsolutdata: true När vi jämförde dessa strängar tidigare utan intern() , var resultatet falskt. Nu kontrollerar intern() -metoden om strängen "CodeGym är den bästa platsen för att lära sig Java!" är i strängpoolen. Naturligtvis är det: vi skapade det med

String s1 = "CodeGym is the best website for learning Java!";
Vi kontrollerar om s1 och referensen som returneras av s2.intern() pekar på samma minnesområde. Och det gör de så klart :) Sammanfattningsvis, memorera och tillämpa denna viktiga regel: använd ALLTID metoden equals() för att jämföra strängar! När vi jämför strängar menar vi nästan alltid att jämföra deras karaktärer snarare än referenser, minnesområden eller något annat. Metoden equals () gör precis vad du behöver. För att förstärka det du lärde dig föreslår vi att du tittar på en videolektion från vår Java-kurs

Mer läsning:

Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION