Usporedba objekata u Javi

1. Uvod

Usporedba objekata je bitna značajka objektno orijentiranih programskih jezika.

U ovom uputstvu ćemo pogledati neke značajke jezika Java koje nam omogućuju usporedbu objekata. Osim toga, takve ćemo značajke pogledati u vanjskim knjižnicama.

2. == i != Operateri

Počnimo s == i != operateri koji mogu utvrditi jesu li dva Java objekta ista ili ne.

2.1. Primitivci

Za primitivne tipove biti isto znači imati jednake vrijednosti:

assertThat (1 == 1) .isTrue ();

Zahvaljujući automatskom raspakiranju, ovo također funkcionira kada se uspoređuje primitivna vrijednost s odgovarajućim tipom omota:

Cijeli broj a = novi cijeli broj (1); assertThat (1 == a) .isTrue ();

Ako dvije cjelobrojne vrijednosti imaju različite vrijednosti, == operater bi se vratio lažno, dok != operater bi se vratio pravi.

2.2. Predmeti

Recimo da želimo usporediti dva Cijeli broj tipovi omota s istom vrijednošću:

Cijeli broj a = novi cijeli broj (1); Cijeli broj b = novi cijeli broj (1); assertThat (a == b) .isFalse ();

Usporedbom dva predmeta, vrijednost tih objekata nije 1. Umjesto da su to njihove memorijske adrese u stogu koji se razlikuju jer su oba objekta stvorena pomoću novi operater. Da smo dodijelili a do b, tada bismo imali drugačiji rezultat:

Cijeli broj a = novi cijeli broj (1); Cijeli broj b = a; assertThat (a == b) .isTrue ();

Sada, da vidimo što će se dogoditi kada koristimo Cijeli broj # vrijednostOf tvornička metoda:

Cijeli broj a = Integer.valueOf (1); Cijeli broj b = Integer.valueOf (1); assertThat (a == b) .isTrue ();

U ovom se slučaju smatraju istim. To je zato što vrijednost() metoda pohranjuje Cijeli broj u predmemoriji kako bi se izbjeglo stvaranje previše objekata omota s istom vrijednošću. Stoga metoda vraća isto Cijeli broj primjer za oba poziva.

Java to također radi za Niz:

assertThat ("Pozdrav!" == "Pozdrav!"). isTrue ();

Međutim, ako su stvoreni pomoću novi tada ne bi bili isti.

Konačno, dva null reference se smatraju istim, dok se svaka ne-null objekt će se smatrati drugačijim od null:

assertThat (null == null) .isTrue (); assertThat ("Pozdrav!" == null) .isFalse ();

Naravno, ponašanje operatora jednakosti može biti ograničavajuće. Što ako želimo usporediti dva objekta mapirana na različite adrese, a opet ih smatrati jednakima na temelju njihovih unutarnjih stanja? Vidjet ćemo kako u sljedećim odjeljcima.

3. Objekt # jednak Metoda

Sada, razgovarajmo o širem konceptu jednakosti s jednako () metoda.

Ova metoda je definirana u Objekt klase tako da ga nasljeđuje svaki Java objekt. Prema zadanim postavkama, Njegova implementacija uspoređuje adrese objektne memorije, pa radi isto kao i == operater. Međutim, ovu metodu možemo nadjačati kako bismo definirali što jednakost znači za naše objekte.

Prvo, pogledajmo kako se ponaša kao postojeći objekti Cijeli broj:

Cijeli broj a = novi cijeli broj (1); Cijeli broj b = novi cijeli broj (1); assertThat (a.equals (b)). isTrue ();

Metoda se i dalje vraća pravi kad su oba predmeta ista.

Trebali bismo napomenuti da možemo proći a null objekt kao argument metode, ali naravno, ne kao objekt na koji pozivamo metodu.

Možemo koristiti jednako () metoda s vlastitim objektom. Recimo da imamo a Osoba razred:

javna klasa Osoba {private String firstName; private String lastName; javna osoba (String firstName, String lastName) {this.firstName = firstName; this.lastName = lastName; }}

Možemo nadjačati jednako () metoda za ovu klasu tako da možemo usporediti dvije Osobas na temelju njihovih internih podataka:

@Override public boolean equals (Object o) if (this == o) return true; if (o == null 

Za više informacija pogledajte naš članak o ovoj temi.

4. Objekti # jednaki Statička metoda

Pogledajmo sada Objekti # jednaki statička metoda. Ranije smo spomenuli da ne možemo koristiti null kao vrijednost prvog predmeta inače a NullPointerException bio bi bačen.

The jednako () metoda Predmeti klasa pomoćnika rješava te probleme. Potrebna su dva argumenta i uspoređuje ih, također obrađujući null vrijednosti.

Usporedimo Osoba ponovo objekt s:

Osoba joe = nova osoba ("Joe", "Portman"); Osoba joeAgain = nova osoba ("Joe", "Portman"); Osoba natalie = nova osoba ("Natalie", "Portman"); assertThat (Objects.equals (joe, joeAgain)). isTrue (); assertThat (Objects.equals (joe, natalie)). isFalse ();

Kao što smo rekli, metoda se nosi null vrijednosti. Stoga, ako su oba argumenta null vratit će se pravi, i ako je samo jedan od njih null, vratit će se lažno.

Ovo može biti vrlo zgodno. Recimo da želimo dodati neobvezni datum rođenja Osoba razred:

javna osoba (String firstName, String lastName, LocalDate birthDate) {this (firstName, lastName); this.birthDate = Datum rođenja; }

Tada bismo morali ažurirati svoj jednako () metoda ali sa null rukovanje. To bismo mogli učiniti dodavanjem ovog uvjeta našem jednako () metoda:

datum rođenja == null? that.birthDate == null: birthDate.equals (that.birthDate);

Međutim, ako svojoj klasi dodamo mnoštvo zabranjenih polja, to može postati stvarno neuredno. Koristiti Objekti # jednaki metoda u našem jednako () implementacija je mnogo čišća i poboljšava čitljivost:

Objects.equals (datum rođenja, datum rođenja);

5. Usporedivo Sučelje

Logika usporedbe također se može koristiti za postavljanje objekata u određenom redoslijedu. The Usporedive sučelje nam omogućuje definiranje redoslijeda između objekata, utvrđivanjem je li objekt veći, jednak ili manji od drugog.

The Usporedive sučelje je generičko i ima samo jednu metodu, compareTo (), koji uzima argument generičkog tipa i vraća int. Vraćena vrijednost negativna je ako ovaj je niži od argumenta, 0 ako su jednaki, a pozitivan u suprotnom.

Recimo, u našem Osoba razreda, želimo usporediti Osoba predmeti po prezimenu:

javna klasa Osoba implementira Usporedivo {// ... @Override public int compareTo (Osoba o) {return this.lastName.compareTo (o.lastName); }}

The compareTo () metoda će vratiti negativan int ako se pozove s Osoba imajući veće prezime od ovaj, nula ako je isto prezime, a pozitivno u suprotnom.

Za više informacija pogledajte naš članak o ovoj temi.

6. Usporednik Sučelje

The Usporednik sučelje je generičko i ima usporedi metoda koja uzima dva argumenta tog generičkog tipa i vraća cijeli broj. Već smo vidjeli taj obrazac s Usporedive sučelje.

Usporednik je sličan; međutim, odvojeno je od definicije klase. Stoga, možemo ih definirati što više Usporednici želimo razred, gdje možemo pružiti samo jedan Usporedive provedba.

Zamislimo da imamo web stranicu koja prikazuje ljude u prikazu tablice i želimo korisniku ponuditi mogućnost da ih razvrstava prema imenima, a ne prema prezimenima. Nije moguće s Usporedive ako također želimo zadržati svoju trenutnu provedbu, ali mogli bismo implementirati svoju Usporednici.

Stvorimo a OsobaUsporednik koja će ih usporediti samo po imenima:

Usporednik compaByFirstNames = Comparator.comparing (Person :: getFirstName);

Razvrstajmo sada a Popis ljudi koji to koriste Usporednik:

Osoba joe = nova osoba ("Joe", "Portman"); Osoba allan = nova osoba ("Allan", "Dale"); Popis ljudi = novi ArrayList (); ljudi.add (joe); ljudi.add (allan); people.sort (compareByFirstNames); assertThat (people) .containsExactly (allan, joe);

Postoje i druge metode na Usporednik sučelje koje možemo koristiti u našem compareTo () provedba:

@Override public int compareTo (Person o) {return Comparator.comparing (Person :: getLastName). ThenComparing (Person :: getFirstName). ThenComparing (Person :: getBirthDate, Comparator.nullsLast (Comparator.naturalOrder ())) .compare ( ovo, o); }

U ovom slučaju prvo uspoređujemo prezimena, a zatim imena. Zatim uspoređujemo datume rođenja, ali kako su oni null, moramo reći kako to riješiti, pa dajemo drugi argument koji govori da ih treba uspoređivati ​​prema njihovom prirodnom redoslijedu, ali s null vrijednosti idu posljednje.

7. Apache Commons

Pogledajmo sada biblioteku Apache Commons. Prije svega, uvezimo ovisnost o Mavenu:

 org.apache.commons commons-lang3 3.10 

7.1. ObjectUtils # notEqual Metoda

Prvo, razgovarajmo o ObjectUtils # notEqual metoda. Treba dva Objekt argumente, kako bi se utvrdilo jesu li jednaki, prema vlastitim jednako () provedba metode. Također se nosi null vrijednosti.

Ponovno iskoristimo naše Niz primjeri:

String a = new String ("Zdravo!"); String b = new String ("Hello World!"); assertThat (ObjectUtils.notEqual (a, b)). isTrue (); 

Treba napomenuti da ObjectUtils ima jednako () metoda. Međutim, to je zastarjelo od Jave 7, kada Objekti # jednaki pojavio

7.2. ObjectUtils # usporedi Metoda

Sad, usporedimo redoslijed objekata s ObjectUtils # usporedi metoda. To je generička metoda koja traje dvoje Usporedive argumente tog generičkog tipa i vraća Cijeli broj.

Da vidimo to koristeći Žice opet:

String first = novi String ("Hello!"); Niz drugi = novi niz ("Kako si?"); assertThat (ObjectUtils.compare (prvi, drugi)). isNegative ();

Prema zadanim postavkama metoda obrađuje null vrijednosti smatrajući ih većima. Nudi preopterećenu verziju koja nudi inverziju tog ponašanja i smatra ih manjim, uzimajući a boolean argument.

8. Guava

Sada, pogledajmo Guavu. Prije svega, uvezimo ovisnost:

 com.google.guava guava 29,0-jre 

8.1. Objekti # jednaki Metoda

Slično knjižnici Apache Commons, Google nam nudi metodu za utvrđivanje jesu li dva objekta jednaka, Objekti # jednaki. Iako imaju različite implementacije, vraćaju iste rezultate:

String a = new String ("Hello!"); String b = novi String ("Pozdrav!"); assertThat (Objects.equal (a, b)). isTrue ();

Iako nije označen kao zastario, JavaDoc ove metode kaže da ga treba smatrati zastarjelim jer Java 7 pruža Objekti # jednaki metoda.

8.2. Metode usporedbe

Sada knjižnica Guava ne nudi metodu za usporedbu dva objekta (u sljedećem ćemo odjeljku vidjeti što možemo učiniti da to postignemo), ali pruža nam metode za usporedbu primitivnih vrijednosti. Uzmimo Ints klase pomoćnika i pogledajte kako je to usporedi () metoda djeluje:

assertThat (Ints.compare (1, 2)). isNegative ();

Kao i obično, vraća cijeli broj to može biti negativno, nula ili pozitivno ako je prvi argument manji, jednak ili veći od drugog. Slične metode postoje za sve primitivne tipove, osim za bajtova.

8.3. Usporedni lanac Razred

Konačno, knjižnica Guava nudi Usporedni lanac klasa koja nam omogućuje uspoređivanje dvaju objekata kroz lanac usporedbi. Lako možemo usporediti dvije Osoba objekti s imenom i prezimenom:

Osoba natalie = nova osoba ("Natalie", "Portman"); Osoba joe = nova osoba ("Joe", "Portman"); int compareResult = ComparisonChain.start () .compare (natalie.getLastName (), joe.getLastName ()) .compare (natalie.getFirstName (), joe.getFirstName ()) .result (); assertThat (compareResult) .isPositive ();

Osnovna usporedba postiže se pomoću compareTo () metodu, pa su argumenti proslijeđeni u usporedi () metode moraju biti primitivne ili Usporedives.

9. Zaključak

U ovom smo članku pogledali različite načine usporedbe objekata u Javi. Ispitali smo razliku između istosti, jednakosti i uređenosti. Također smo pogledali odgovarajuće značajke u knjižnicama Apache Commons i Guava.

Kao i obično, puni kod za ovaj članak nalazi se na GitHubu.