Sammenligne objekter i Java

1. Introduksjon

Å sammenligne objekter er et viktig trekk ved objektorienterte programmeringsspråk.

I denne opplæringen skal vi se på noen av funksjonene i Java-språket som lar oss sammenligne objekter. I tillegg vil vi se på slike funksjoner i eksterne biblioteker.

2. == og != Operatører

La oss begynne med == og != operatører som kan fortelle om henholdsvis to Java-objekter er like eller ikke.

2.1. Primitiver

For primitive typer betyr det å ha like verdier å være det samme:

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

Takket være automatisk utpakking, dette fungerer også når man sammenligner en primitiv verdi med motstykket til innpakningstypen:

Heltall a = nytt Heltall (1); assertThat (1 == a) .isTrue ();

Hvis to heltall har forskjellige verdier, blir == operatøren ville komme tilbake falsk, mens != operatøren ville komme tilbake ekte.

2.2. Objekter

La oss si at vi vil sammenligne to Heltall innpakningstyper med samme verdi:

Heltall a = nytt Heltall (1); Heltall b = nytt Heltall (1); assertThat (a == b) .isFalse ();

Ved å sammenligne to objekter, verdien av disse objektene er ikke 1. Det er heller deres minneadresser i bunken som er forskjellige siden begge objektene ble opprettet ved hjelp av ny operatør. Hvis vi hadde tildelt en til b, så hadde vi hatt et annet resultat:

Heltall a = nytt Heltall (1); Heltall b = a; assertThat (a == b) .isTrue ();

La oss nå se hva som skjer når vi bruker Heltall # verdi Av fabrikk metode:

Integer a = Integer.valueOf (1); Heltall b = Heltall.verdiOf (1); assertThat (a == b) .isTrue ();

I dette tilfellet blir de ansett som det samme. Dette er fordi verdien av() metoden lagrer Heltall i en hurtigbuffer for å unngå å lage for mange wrapperobjekter med samme verdi. Derfor returnerer metoden den samme Heltall forekomst for begge samtalene.

Java gjør dette også for String:

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

Imidlertid, hvis de ble opprettet ved hjelp av ny operatør, da ville de ikke være de samme.

Endelig, to null referanser anses å være de samme, mens alle ikke-null objektet vil bli ansett som forskjellig fra null:

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

Selvfølgelig kan oppførselen til likhetsoperatørene være begrensende. Hva om vi vil sammenligne to objekter som er kartlagt til forskjellige adresser, og likevel å ha dem ansett som like, basert på deres interne tilstander? Vi får se hvordan i de neste avsnittene.

3. Objekt # er lik Metode

La oss nå snakke om et bredere begrep om likhet med er lik() metode.

Denne metoden er definert i Gjenstand klasse slik at hvert Java-objekt arver det. Som standard, implementeringen sammenligner objektminneadresser, så det fungerer det samme som == operatør. Vi kan imidlertid overstyre denne metoden for å definere hva likhet betyr for objektene våre.

La oss først se hvordan den oppfører seg for eksisterende objekter som Heltall:

Heltall a = nytt Heltall (1); Heltall b = nytt Heltall (1); hevder at (a. likhet (b)). er sann ();

Metoden returnerer fortsatt ekte når begge objektene er like.

Vi bør merke oss at vi kan passere en null objekt som argumentet for metoden, men selvfølgelig ikke som objektet vi kaller metoden på.

Vi kan bruke er lik() metode med et eget objekt. La oss si at vi har en Person klasse:

offentlig klasse Person {privat streng fornavn; privat streng etternavn; offentlig person (streng fornavn, streng etternavn) {this.firstName = fornavn; this.lastName = etternavn; }}

Vi kan overstyre er lik() metode for denne klassen slik at vi kan sammenligne to Persons basert på deres interne detaljer:

@ Overstyr offentlig boolsk er lik (Objekt o) hvis (dette == o) returnerer sant; hvis (o == null 

For mer informasjon, se vår artikkel om dette emnet.

4. Objekter # er like Statisk metode

La oss nå se på Objekter # er lik statisk metode. Vi nevnte tidligere at vi ikke kan bruke null som verdien av det første objektet ellers a NullPointerException ville bli kastet.

De er lik() metoden for Objekter hjelperklasse løser disse problemene. Det tar to argumenter og sammenligner dem, også håndtering null verdier.

La oss sammenligne Person gjenstander igjen med:

Person joe = ny person ("Joe", "Portman"); Person joeAgain = ny person ("Joe", "Portman"); Person natalie = ny person ("Natalie", "Portman"); assertThat (Objects.equals (joe, joeAgain)). isTrue (); assertThat (Objects.equals (joe, natalie)). isFalse ();

Som vi takler, håndterer metoden null verdier. Derfor, hvis begge argumentene er null det kommer tilbake ekte, og hvis bare en av dem er det null, kommer den tilbake falsk.

Dette kan være veldig nyttig. La oss si at vi vil legge til en valgfri fødselsdato til vår Person klasse:

offentlig person (streng fornavn, streng etternavn, LocalDate fødselsdato) {dette (fornavn, etternavn); this.birthDate = fødselsdato; }

Da må vi oppdatere er lik() metode men med null håndtering. Vi kan gjøre dette ved å legge denne tilstanden til vår er lik() metode:

fødselsdato == null? that.birthDate == null: birthDate.equals (that.birthDate);

Men hvis vi legger til mange nullaktive felt i klassen vår, kan det bli veldig rotete. Bruker Objekter # er lik metode i vår er lik() implementeringen er mye renere, og forbedrer lesbarheten:

Objects.equals (birthDate, that.birthDate);

5. Sammenlignelig Grensesnitt

Sammenligningslogikk kan også brukes til å plassere objekter i en bestemt rekkefølge. De Sammenlignelig grensesnitt lar oss definere en rekkefølge mellom objekter, ved å bestemme om et objekt er større, lik eller mindre enn et annet.

De Sammenlignelig grensesnittet er generisk og har bare en metode, sammenligne med(), som tar et argument av den generiske typen og returnerer et int. Den returnerte verdien er negativ hvis dette er lavere enn argumentet, 0 hvis de er like, og ellers positive.

La oss si, i vår Person klasse, vil vi sammenligne Person gjenstander med etternavn:

offentlig klasse Person implementerer Sammenlignbar {// ... @Override public int compareTo (Person o) {return this.lastName.compareTo (o.lastName); }}

De sammenligne med() metoden vil returnere et negativt int hvis ringt med en Person har et større etternavn enn dette, null hvis samme etternavn, og positivt ellers.

For mer informasjon, ta en titt på artikkelen vår om dette emnet.

6. Komparator Grensesnitt

De Komparator grensesnittet er generisk og har en sammenligne metode som tar to argumenter av den generiske typen og returnerer en heltall. Vi så allerede det mønsteret tidligere med Sammenlignelig grensesnitt.

Komparator er lik; det er imidlertid skilt fra definisjonen av klassen. Derfor, vi kan definere så mange Sammenligning vi ønsker en klasse der vi bare kan tilby en Sammenlignelig gjennomføring.

La oss forestille oss at vi har en webside som viser folk i en tabellvisning, og vi vil gi brukeren muligheten til å sortere dem etter fornavn i stedet for etternavn. Det er ikke mulig med Sammenlignelig hvis vi også vil beholde vår nåværende implementering, men vi kan implementere vår egen Sammenligning.

La oss lage en PersonKomparator som bare vil sammenligne dem med fornavn:

Comparator CompareByFirstNames = Comparator.comparing (Person :: getFirstName);

La oss nå sortere a Liste av folk som bruker det Komparator:

Person joe = ny person ("Joe", "Portman"); Person allan = ny person ("Allan", "Dale"); Liste folk = ny ArrayList (); people.add (joe); folk.add (allan); people.sort (jämförByFirstNames); assertThat (folk) .containsExactly (allan, joe);

Det er andre metoder på Komparator grensesnitt vi kan bruke i vårt sammenligne med() gjennomføring:

@Override public int CompareTo (Person o) {return Comparator.comparing (Person :: getLastName) .thenComparing (Person :: getFirstName) .thenComparing (Person :: getBirthDate, Comparator.nullsLast (Comparator.naturalOrder ())) .compare ( dette, o); }

I dette tilfellet sammenligner vi først etternavn, deretter fornavn. Deretter sammenligner vi fødselsdatoer, men da de er ugyldige, må vi si hvordan vi skal håndtere det, så vi gir et annet argument som forteller at de skal sammenlignes i henhold til deres naturlige orden, men med null verdiene går sist.

7. Apache Commons

La oss nå ta en titt på Apache Commons-biblioteket. La oss først importere Maven-avhengigheten:

 org.apache.commons commons-lang3 3.10 

7.1. ObjectUtils # notEqual Metode

La oss først snakke om ObjectUtils # notEqual metode. Det krever to Gjenstand argumenter, for å avgjøre om de ikke er like, i henhold til sine egne er lik() metodeimplementering. Den håndterer også null verdier.

La oss bruke vårt String eksempler:

Streng a = ny streng ("Hei!"); Streng b = ny streng ("Hello World!"); assertThat (ObjectUtils.notEqual (a, b)). isTrue (); 

Det er verdt å merke seg at ObjectUtils har en er lik() metode. Det er imidlertid avviklet siden Java 7, når Objekter # er lik dukket opp

7.2. ObjectUtils # sammenlign Metode

La oss nå sammenligne objektrekkefølgen med ObjectUtils # sammenlign metode. Det er en generisk metode som tar to Sammenlignelig argumenter av den generiske typen og returnerer en Heltall.

La oss se at bruk Strenger en gang til:

Streng først = ny streng ("Hei!"); Streng sekund = ny streng ("Hvordan har du det?"); assertThat (ObjectUtils.compare (første, andre)). er Negativ ();

Som standard håndteres metoden null verdier ved å betrakte dem som større. Det tilbyr en overbelastet versjon som tilbyr å invertere atferden og vurdere dem som mindre, og ta en boolsk argument.

8. Guava

La oss ta en titt på Guava. La oss først importere avhengigheten:

 com.google.guava guava 29.0-jre 

8.1. Objekter # like Metode

I likhet med Apache Commons-biblioteket gir Google oss en metode for å bestemme om to objekter er like, Objekter # like. Selv om de har forskjellige implementeringer, gir de de samme resultatene:

Streng a = ny streng ("Hei!"); Streng b = ny streng ("Hei!"); assertThat (Objects.equal (a, b)). isTrue ();

Selv om den ikke er merket som utfaset, sier JavaDoc av denne metoden at den skal betraktes som utfaset siden Java 7 gir Objekter # er lik metode.

8.2. Sammenligningsmetoder

Nå tilbyr Guava-biblioteket ikke en metode for å sammenligne to objekter (vi ser i neste avsnitt hva vi kan gjøre for å oppnå det skjønt), men det gir oss metoder for å sammenligne primitive verdier. La oss ta Ints hjelperklasse og se hvordan dens sammenligne() metoden fungerer:

assertThat (Ints.compare (1, 2)). er Negative ();

Som vanlig returnerer den en heltall det kan være negativt, null eller positivt hvis det første argumentet er henholdsvis mindre, lik eller større enn det andre. Lignende metoder eksisterer for alle primitive typer, bortsett fra byte.

8.3. ComparisonChain Klasse

Til slutt tilbyr Guava biblioteket ComparisonChain klasse som lar oss sammenligne to objekter gjennom en kjede av sammenligninger. Vi kan enkelt sammenligne to Person objekter med for- og etternavn:

Person natalie = ny person ("Natalie", "Portman"); Person joe = ny person ("Joe", "Portman"); int comparisonResult = ComparisonChain.start () .compare (natalie.getLastName (), joe.getLastName ()). sammenligne (natalie.getFirstName (), joe.getFirstName ()) .result (); assertThat (comparisonResult) .isPositive ();

Den underliggende sammenligningen oppnås ved hjelp av sammenligne med() metoden, så argumentene ble sendt til sammenligne() metoder må enten være primitive eller Sammenligneligs.

9. Konklusjon

I denne artikkelen så vi på de forskjellige måtene å sammenligne objekter i Java. Vi undersøkte forskjellen mellom likhet, likhet og orden. Vi har også sett på de tilsvarende funksjonene i Apache Commons og Guava-bibliotekene.

Som vanlig kan du finne den fulle koden for denne artikkelen på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found