JavaRush /Blog Java /Random-PL /Równa się w Javie i porównaniu ciągów — porównanie ciągów...

Równa się w Javie i porównaniu ciągów — porównanie ciągów

Opublikowano w grupie Random-PL
Cześć! Dzisiaj porozmawiamy o bardzo ważnym i interesującym temacie, a mianowicie porównywaniu obiektów ze sobą równa się() w Javie. I rzeczywiście, w jakich przypadkach w Javie Obiekt A będzie równy Obiektowi B ? Równania i porównanie ciągów - 1Spróbujmy napisać przykład:
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);
   }
}
Wyjście konsoli:

false
OK, przestań. Dlaczego w rzeczywistości te dwa samochody nie są sobie równe? Nadaliśmy im te same właściwości, ale wynik porównania jest fałszywy. Odpowiedź jest prosta. Operator ==porównuje nie właściwości obiektów, ale łącza. Nawet jeśli dwa obiekty mają 500 identycznych właściwości, wynik porównania i tak będzie fałszywy. Przecież linki car1prowadzą car2 do dwóch różnych obiektów , do dwóch różnych adresów. Wyobraź sobie sytuację, w której porównujesz ludzi. Prawdopodobnie jest na świecie osoba, która ma takie samo imię, kolor oczu, wiek, wzrost, kolor włosów itp. co Ty. Oznacza to, że jesteście podobni pod wieloma względami, ale nadal nie jesteście bliźniakami, a zwłaszcza nie tą samą osobą. Równania i porównanie ciągów - 2Operator stosuje w przybliżeniu tę samą logikę, ==gdy używamy go do porównywania dwóch obiektów. Ale co, jeśli potrzebujesz innej logiki w swoim programie? Na przykład, jeśli Twój program symuluje analizę DNA. Musi porównać kod DNA dwojga ludzi i ustalić, że są bliźniakami.
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);
   }
}
Wyjście konsoli:

false
Logiczne jest, że wynik był taki sam (w końcu nic nie zmienialiśmy), ale teraz nie jesteśmy z tego zadowoleni! Rzeczywiście, w prawdziwym życiu analiza DNA jest stuprocentową gwarancją, że mamy do czynienia z bliźniakami. Ale nasz program i operator ==mówią nam co innego. Jak możemy zmienić to zachowanie i mieć pewność, że jeśli testy DNA będą zgodne, program da prawidłowy wynik? W tym celu stworzono w Javie specjalną metodę -quals() .

Metoda Equals() w Javie

Podobnie jak metoda, toString()którą omawialiśmy wcześniej, metodaquals() należy do klasy, Objectnajważniejszej klasy w Javie, z której wywodzą się wszystkie pozostałe klasy. Jednak sama funkcjaquals() nie zmieni w żaden sposób zachowania naszego programu:
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));
   }
}
Wyjście konsoli:

false
Dokładnie taki sam wynik, więc po co więc ta metoda? :/ To proste. Faktem jest, że teraz użyliśmy tej metody, ponieważ jest ona zaimplementowana w samej klasie Object. A jeśli wejdziemy w kod klasy Objecti przyjrzymy się, jak ta metoda jest w nim zaimplementowana i co robi, zobaczymy:
public boolean equals(Object obj) {
   return (this == obj);
}
To jest powód, dla którego zachowanie naszego programu się nie zmieniło! Wewnątrz metody równości() tej klasy Objectznajduje się to samo porównanie referencji, ==. Ale sztuczka tej metody polega na tym, że możemy ją zastąpić. Zastępowanie oznacza napisanie własnej metody równości() w naszej klasie Mani sprawienie, aby zachowywała się tak, jak chcemy! Nie jesteśmy już przekonani, że sprawdzenie man1.equals(man2)zasadniczo robi to samo, co man1 == man2. Oto, co zrobimy w tej sytuacji:
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));

   }
}
Wyjście konsoli:

true
Zupełnie inny wynik! Pisząc własną metodę równości() zamiast standardowej uzyskaliśmy prawidłowe zachowanie: teraz jeśli dwie osoby mają ten sam kod DNA, program mówi nam: „Analiza DNA wykazała, że ​​są bliźniakami” i zwraca prawdę! Zastępując metodę równości() w swoich klasach, możesz łatwo utworzyć niezbędną logikę porównywania obiektów. O porównaniu obiektów poruszyliśmy jedynie w sposób ogólny. Przed nami jeszcze osobny, duży wykład na ten temat (możesz go teraz szybko przeczytać, jeśli jesteś zainteresowany).

Porównanie ciągów w Javie - Porównanie ciągów

Dlaczego porównania ciągów traktujemy oddzielnie od wszystkiego innego? Cóż, w rzeczywistości linie w programowaniu to zupełnie inna historia. Po pierwsze, jeśli weźmie się pod uwagę wszystkie programy Java napisane przez ludzkość, okaże się, że składa się z nich około 25% obiektów w nich zawartych. Dlatego ten temat jest bardzo ważny. Po drugie, proces porównywania ciągów znaków naprawdę różni się od procesu porównywania innych obiektów. Spójrzmy na prosty przykład:
public class Main {

   public static void main(String[] args) {

       String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s2 = new String(„JavaRush to najlepsza strona do nauki języka Java!”);
       System.out.println(s1 == s2);
   }
}
Wyjście konsoli:

false
Ale dlaczego fałszywe? Linie są dokładnie takie same, słowo w słowo :/ Można założyć: dzieje się tak dlatego, że operator == porównuje referencje! W końcu s1mają s2różne adresy w pamięci. Jeśli przyszła Ci do głowy taka myśl, powtórzmy nasz przykład:
public class Main {

   public static void main(String[] args) {

       String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s2 = „JavaRush to najlepsza strona do nauki języka Java!”;
       System.out.println(s1 == s2);
   }
}
Teraz mamy również dwa linki, ale wynik zmienił się na odwrotny: Dane wyjściowe konsoli:

true
Całkowicie zdezorientowany? :) Rozwiążmy to. Operator ==faktycznie porównuje adresy w pamięci. Ta zasada zawsze się sprawdza i nie ma powodu w to wątpić. Oznacza to, że jeśli s1 == s2zwróci wartość true, te dwa ciągi będą miały ten sam adres w pamięci. I rzeczywiście tak jest! Czas zapoznać się ze specjalnym obszarem pamięci do przechowywania ciągów znaków - pulą ciągów ( String pool) Równania i porównanie ciągów - 3Pula ciągów to obszar do przechowywania wszystkich wartości ciągów, które utworzysz w swoim programie. Po co został stworzony? Jak wspomniano wcześniej, łańcuchy zajmują ogromną część wszystkich obiektów. W każdym dużym programie powstaje wiele linii. Aby zaoszczędzić pamięć, potrzebne jest to String Pool- tam umieszcza się wiersz z potrzebnym tekstem, a w przyszłości nowo utworzone linki będą odnosić się do tego samego obszaru pamięci, nie ma potrzeby każdorazowego alokowania dodatkowej pamięci. Za każdym razem, gdy piszesz String = “........”, program sprawdza, czy w puli ciągów znajduje się linia z takim tekstem. Jeśli tak, nowy nie zostanie utworzony. Nowe łącze będzie wskazywało ten sam adres w puli ciągów, w której przechowywany jest ten ciąg. Dlatego kiedy pisaliśmy w programie
String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
String s2 = „JavaRush to najlepsza strona do nauki języka Java!”;
link s2wskazuje dokładnie to samo miejsce co s1. Pierwsze polecenie utworzyło w puli ciągów nową linię z potrzebnym tekstem, a drugie po prostu odnosiło się do tego samego obszaru pamięci co s1. Możesz utworzyć co najmniej 500 linii więcej z tym samym tekstem, wynik się nie zmieni. Zatrzymywać się. Ale dlaczego ten przykład nie zadziałał dla nas wcześniej?
public class Main {

   public static void main(String[] args) {

       String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s2 = new String(„JavaRush to najlepsza strona do nauki języka Java!”);
       System.out.println(s1 == s2);
   }
}
Myślę, że intuicyjnie już domyślasz się, jaka jest tego przyczyna :) Spróbuj zgadnąć, zanim zaczniesz czytać dalej. Widać, że te dwie linie zostały utworzone inaczej. Jeden jest z pomocą operatora new, a drugi bez niego. To jest właśnie powód. Operator new podczas tworzenia obiektu na siłę przydziela mu nowy obszar w pamięci . A linia utworzona za pomocą new, nie kończy się w String Pool: staje się oddzielnym obiektem, nawet jeśli jego tekst jest dokładnie taki sam, jak ten sam wiersz z String Pool„a”. Oznacza to, że jeśli napiszemy następujący kod:
public class Main {

   public static void main(String[] args) {

       String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s2 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s3 = new String(„JavaRush to najlepsza strona do nauki języka Java!”);
   }
}
W pamięci będzie to wyglądać tak: Równania i porównanie ciągów - 4Za każdym razem, gdy tworzony jest nowy obiekt, neww pamięci zostanie przydzielony nowy obszar, nawet jeśli tekst w nowych wierszach jest taki sam! Wygląda na to, że uporaliśmy się z operatorem ==, ale co z naszym nowym przyjacielem – metodą równości()?
public class Main {

   public static void main(String[] args) {

       String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s2 = new String(„JavaRush to najlepsza strona do nauki języka Java!”);
       System.out.println(s1.equals(s2));
   }
}
Wyjście konsoli:

true
Ciekawy. Wiemy dokładnie co s1i s2wskazujemy na różne obszary pamięci. Niemniej jednak metoda równości() mówi, że są one równe. Dlaczego? Pamiętasz, jak powiedzieliśmy powyżej, że metodę równości() można zastąpić w Twojej klasie, aby porównuje obiekty tak, jak potrzebujesz? To właśnie zrobili z klasą String. Ma zastąpioną metodę równości(). I porównuje nie linki, ale raczej sekwencję znaków w łańcuchach. A jeśli tekst w ciągach znaków jest taki sam, nie ma znaczenia, w jaki sposób zostały utworzone i gdzie są przechowywane: w puli ciągów, czy w oddzielnym obszarze pamięci. Wynik porównania będzie prawdziwy. Nawiasem mówiąc, Java umożliwia prawidłowe porównywanie ciągów znaków bez uwzględniania wielkości liter. W normalnej sytuacji, jeśli napiszesz jedną z linijek, na przykład wielkimi literami, wynik porównania będzie fałszywy:
public class Main {

   public static void main(String[] args) {

       String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s2 = new String("JAVARUSH - ЛУЧШИЙ САЙТ ДЛЯ ИЗУЧЕНИЯ JAVA!");
       System.out.println(s1.equals(s2));
   }
}
Wyjście konsoli:

false
W tym przypadku klasa Stringma metodę equalsIgnoreCase(). Jeśli w Twoim porównaniu najważniejsza jest sekwencja określonych znaków, a nie ich wielkość, możesz z tego skorzystać. Przyda się to na przykład przy porównywaniu dwóch adresów e-mail:
public class Main {

   public static void main(String[] args) {

       String address1 = „Moskwa, ulica Akademika Korolewa, 12”;
       String address2 = new String("Г. МОСКВА, УЛ. АКАДЕМИКА КОРОЛЕВА, ДОМ 12");
       System.out.println(address1.equalsIgnoreCase(address2));
   }
}
W tym przypadku oczywiste jest, że mówimy o tym samym adresie, więc zastosowanie tej metody equalsIgnoreCase()będzie słuszną decyzją.

Metoda String.intern().

Klasa Stringma inną trudną metodę - intern(); Metoda intern()działa bezpośrednio z String Pool'om. Jeśli wywołasz metodę intern()na ciągu znaków, to:
  • Sprawdza, czy w puli ciągów znajduje się ciąg znaków zawierający ten tekst
  • Jeśli istnieje, zwraca łącze do niego w puli
  • Jeśli nie, umieszcza linię z tym tekstem w puli ciągów i zwraca łącze do niego.
Stosując tę ​​metodę intern()do odniesienia do ciągu znaków utworzonego za pomocą polecenia new, możemy porównać je z odwołaniem do ciągu znaków z String Pool„a za pośrednictwem ==.
public class Main {

   public static void main(String[] args) {

       String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
       String s2 = new String(„JavaRush to najlepsza strona do nauki języka Java!”);
       System.out.println(s1 == s2.intern());
   }
}
Wyjście konsoli:

true
Poprzednio, gdy porównywaliśmy je bez intern(), wynik był fałszywy. Teraz metoda intern()sprawdzała, czy pojawiła się linia z tekstem „JavaRush - najlepsza strona do nauki języka Java!” w puli strun. Oczywiście, że tam jest: stworzyliśmy ją, kiedy pisaliśmy
String s1 = „JavaRush to najlepsza strona do nauki języka Java!”;
Sprawdzono, czy referencja s1i referencja zwrócona przez metodę s2.intern()wskazują na ten sam obszar w pamięci i oczywiście tak jest :) Podsumowując, pamiętaj i stosuj główną zasadę: Do porównywania ciągów ZAWSZE używaj funkcji równości() metoda! Porównując ciągi znaków, prawie zawsze masz na myśli porównywanie ich tekstu, a nie łączy, obszarów pamięci i tak dalej. Metoda równości() robi dokładnie to, czego potrzebujesz. Oto kilka linków do samodzielnej nauki:
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION