Objecten in Java vergelijken

1. Inleiding

Het vergelijken van objecten is een essentieel kenmerk van objectgeoriënteerde programmeertalen.

In deze zelfstudie gaan we kijken naar enkele functies van de Java-taal waarmee we objecten kunnen vergelijken. Bovendien zullen we dergelijke functies in externe bibliotheken bekijken.

2. == en != Operatoren

Laten we beginnen met de == en != operators die kunnen zien of twee Java-objecten respectievelijk hetzelfde zijn of niet.

2.1. Primitieven

Voor primitieve typen betekent hetzelfde zijn dat ze gelijke waarden hebben:

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

Dankzij auto-unboxing, dit werkt ook bij het vergelijken van een primitieve waarde met zijn tegenhanger van het wrapper-type:

Geheel getal a = nieuw geheel getal (1); assertThat (1 == a) .isTrue ();

Als twee gehele getallen verschillende waarden hebben, is de == operator zou terugkeren false, Terwijl de != operator zou terugkeren waar.

2.2. Voorwerpen

Laten we zeggen dat we er twee willen vergelijken Geheel getal wrapper-typen met dezelfde waarde:

Geheel getal a = nieuw geheel getal (1); Geheel getal b = nieuw geheel getal (1); assertThat (a == b) .isFalse ();

Door twee objecten te vergelijken, de waarde van die objecten is niet 1. Het zijn eerder hun geheugenadressen in de stapel die verschillend zijn omdat beide objecten zijn gemaakt met de nieuw operator. Als we hadden toegewezen een naar b, dan hadden we een ander resultaat gehad:

Geheel getal a = nieuw geheel getal (1); Geheel getal b = a; assertThat (a == b) .isTrue ();

Laten we nu eens kijken wat er gebeurt als we de Geheel getal # waardeVan fabrieks methode:

Geheel getal a = Integer.valueOf (1); Geheel getal b = Geheel getal.valueOf (1); assertThat (a == b) .isTrue ();

In dit geval worden ze als hetzelfde beschouwd. Dit komt doordat de waarde van() methode slaat het Geheel getal in een cache om te voorkomen dat er te veel wrapper-objecten met dezelfde waarde worden gemaakt. Daarom retourneert de methode hetzelfde Geheel getal bijvoorbeeld voor beide oproepen.

Java doet dit ook voor Draad:

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

Als ze echter zijn gemaakt met de nieuw operator, dan zouden ze niet hetzelfde zijn.

Tenslotte, twee nul referenties worden als hetzelfde beschouwd, terwijl alle niet-nul object wordt als anders beschouwd dan nul:

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

Het gedrag van de gelijkheidsoperatoren kan natuurlijk beperkend zijn. Wat als we twee objecten die zijn toegewezen aan verschillende adressen willen vergelijken en ze toch als gelijk willen beschouwen op basis van hun interne toestand? We zullen zien hoe in de volgende secties.

3. Object # is gelijk aan Methode

Laten we het nu hebben over een breder concept van gelijkheid met de is gelijk aan () methode.

Deze methode is gedefinieerd in het Voorwerp class zodat elk Java-object het erft. Standaard, de implementatie vergelijkt objectgeheugenadressen, dus het werkt hetzelfde als de == operator. We kunnen deze methode echter negeren om te bepalen wat gelijkheid betekent voor onze objecten.

Laten we eerst eens kijken hoe het zich gedraagt ​​voor bestaande objecten, zoals Geheel getal:

Geheel getal a = nieuw geheel getal (1); Geheel getal b = nieuw geheel getal (1); assertThat (a.equals (b)). isTrue ();

De methode keert nog steeds terug waar als beide objecten hetzelfde zijn.

We moeten er rekening mee houden dat we een nul object als het argument van de methode, maar natuurlijk niet als het object waarop we de methode aanroepen.

We kunnen de is gelijk aan () methode met een eigen object. Laten we zeggen dat we een Persoon klasse:

openbare klasse Persoon {privé String firstName; private String achternaam; publieke persoon (String voornaam, String achternaam) {this.firstName = firstName; this.lastName = achternaam; }}

We kunnen het is gelijk aan () methode voor deze klasse, zodat we er twee kunnen vergelijken Persoons op basis van hun interne gegevens:

@Override public boolean is gelijk aan (Object o) if (this == o) retourneert true; if (o == null 

Zie ons artikel over dit onderwerp voor meer informatie.

4. Objecten # is gelijk aan Statische methode

Laten we nu eens kijken naar de Objecten # is gelijk aan statische methode. We zeiden eerder dat we niet kunnen gebruiken nul als de waarde van het eerste object, anders a NullPointerException zou worden gegooid.

De is gelijk aan () methode van de Voorwerpen helper class lost die problemen op. Het duurt twee argumenten en vergelijkt ze, ook de afhandeling nul waarden.

Laten we vergelijken Persoon objecten weer met:

Persoon joe = nieuwe persoon ("Joe", "Portman"); Persoon joeAgain = nieuwe persoon ("Joe", "Portman"); Persoon natalie = nieuwe persoon ("Natalie", "Portman"); assertThat (Objects.equals (joe, joeAgain)). isTrue (); assertThat (Objects.equals (joe, natalie)). isFalse ();

Zoals we al zeiden, handelt de methode nul waarden. Daarom, als beide argumenten zijn nul het zal terugkeren waar, en als er maar één is nul, het zal terugkeren false.

Dit kan erg handig zijn. Laten we zeggen dat we een optionele geboortedatum aan onze willen toevoegen Persoon klasse:

public Person (String firstName, String lastName, LocalDate birthDate) {this (firstName, lastName); this.birthDate = birthDate; }

Dan zouden we onze is gelijk aan () methode maar met nul behandeling. We zouden dit kunnen doen door deze voorwaarde toe te voegen aan onze is gelijk aan () methode:

birthDate == null? that.birthDate == null: birthDate.equals (that.birthDate);

Als we echter veel velden met nullabels aan onze klas toevoegen, kan het erg rommelig worden. De ... gebruiken Objecten # is gelijk aan methode in onze is gelijk aan () implementatie is veel schoner en verbetert de leesbaarheid:

Objects.equals (birthDate, that.birthDate);

5. vergelijkbaar Koppel

Vergelijkingslogica kan ook worden gebruikt om objecten in een specifieke volgorde te plaatsen. De Vergelijkbaar interface stelt ons in staat om een ​​ordening tussen objecten te definiëren, door te bepalen of een object groter, gelijk of kleiner is dan een ander.

De Vergelijkbaar interface is generiek en heeft maar één methode, vergelijk met(), die een argument van het generieke type accepteert en een retourneert int. De geretourneerde waarde is negatief als dit is lager dan het argument, 0 als ze gelijk zijn en anders positief.

Laten we zeggen, in onze Persoon klasse, we willen vergelijken Persoon objecten op achternaam:

public class Person implementeert Vergelijkbaar {// ... @Override public int CompareTo (Person o) {return this.lastName.compareTo (o.lastName); }}

De vergelijk met() methode retourneert een negatief int indien gebeld met een Persoon met een grotere achternaam dan dit, nul als dezelfde achternaam en anders positief.

Bekijk ons ​​artikel over dit onderwerp voor meer informatie.

6. Vergelijker Koppel

De Comparator interface is generiek en heeft een vergelijken methode waaraan twee argumenten van dat generieke type moeten doorgegeven worden en een geheel getal. Dat patroon zagen we al eerder bij de Vergelijkbaar koppel.

Comparator is soortgelijk; het is echter gescheiden van de definitie van de klasse. Daarom we kunnen er zoveel definiëren Vergelijkers we willen voor een klas, waar we er maar één kunnen geven Vergelijkbaar implementatie.

Laten we ons voorstellen dat we een webpagina hebben met mensen in een tabelweergave, en we willen de gebruiker de mogelijkheid bieden om ze op voornaam te sorteren in plaats van op achternaam. Het is niet mogelijk met Vergelijkbaar als we ook onze huidige implementatie willen behouden, maar we zouden onze eigen kunnen implementeren Vergelijkers.

Laten we een PersoonComparator die ze alleen met hun voornaam zullen vergelijken:

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

Laten we nu een Lijst van de mensen die dat gebruiken Comparator:

Persoon joe = nieuwe persoon ("Joe", "Portman"); Persoon allan = nieuwe persoon ("Allan", "Dale"); List people = new ArrayList (); people.add (joe); people.add (allan); people.sort (vergelijkByFirstNames); assertThat (mensen) .containsExactly (allan, joe);

Er zijn andere methoden op het Comparator interface die we kunnen gebruiken in onze vergelijk met() implementatie:

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

In dit geval vergelijken we eerst de achternaam en vervolgens de voornamen. Vervolgens vergelijken we geboortedata, maar omdat ze nul kunnen worden, moeten we zeggen hoe we daarmee om moeten gaan, dus geven we een tweede argument om te zeggen dat ze moeten worden vergeleken volgens hun natuurlijke volgorde, maar met nul waarden gaan als laatste.

7. Apache Commons

Laten we nu eens kijken naar de Apache Commons-bibliotheek. Laten we eerst de Maven-afhankelijkheid importeren:

 org.apache.commons commons-lang3 3.10 

7.1. ObjectUtils # notEqual Methode

Laten we eerst praten over de ObjectUtils # notEqual methode. Er zijn er twee nodig Voorwerp argumenten, om te bepalen of ze niet gelijk zijn, volgens hun eigen argumenten is gelijk aan () methode implementatie. Het behandelt ook nul waarden.

Laten we onze Draad voorbeelden:

String a = new String ("Hallo!"); String b = new String ("Hallo wereld!"); assertThat (ObjectUtils.notEqual (a, b)). isTrue (); 

het zou genoteerd moeten worden dat ObjectUtils heeft een is gelijk aan () methode. Dat is echter verouderd sinds Java 7, wanneer Objecten # is gelijk aan verscheen

7.2. ObjectUtils # vergelijken Methode

Laten we nu de objectvolgorde vergelijken met de ObjectUtils # vergelijken methode. Het is een generieke methode waarvoor er twee nodig zijn Vergelijkbaar argumenten van dat generieke type en retourneert een Geheel getal.

Laten we eens kijken met behulp van Snaren opnieuw:

String first = nieuwe String ("Hallo!"); String second = new String ("Hoe gaat het?"); assertThat (ObjectUtils.compare (eerste, tweede)). isNegative ();

Standaard handelt de methode nul waarden door ze als groter te beschouwen. Het biedt een overbelaste versie die aanbiedt om dat gedrag om te keren en ze als minder te beschouwen, door een boolean argument.

8. Guave

Laten we nu eens naar Guava kijken. Laten we eerst de afhankelijkheid importeren:

 com.google.guava guave 29.0-jre 

8.1. Objecten # gelijk Methode

Vergelijkbaar met de Apache Commons-bibliotheek, biedt Google ons een methode om te bepalen of twee objecten gelijk zijn, Objecten # gelijk. Hoewel ze verschillende implementaties hebben, retourneren ze dezelfde resultaten:

String a = new String ("Hallo!"); String b = nieuwe String ("Hallo!"); assertThat (Objects.equal (a, b)). isTrue ();

Hoewel het niet is gemarkeerd als verouderd, zegt de JavaDoc van deze methode dat het als verouderd moet worden beschouwd omdat Java 7 de Objecten # is gelijk aan methode.

8.2. Vergelijkingsmethoden

Nu biedt de Guava-bibliotheek geen methode om twee objecten te vergelijken (we zullen in de volgende sectie zien wat we kunnen doen om dat te bereiken), maar het biedt ons methoden om primitieve waarden te vergelijken. Laten we de Ints helper class en kijk hoe het is vergelijken() methode werkt:

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

Zoals gewoonlijk retourneert het een geheel getal dat kan negatief, nul of positief zijn als het eerste argument respectievelijk kleiner, gelijk of groter is dan het tweede. Vergelijkbare methoden bestaan ​​voor alle primitieve typen, behalve voor bytes.

8.3. VergelijkingChain Klasse

Ten slotte biedt de Guava-bibliotheek de VergelijkingChain klasse waarmee we twee objecten kunnen vergelijken door middel van een reeks vergelijkingen. We kunnen er gemakkelijk twee vergelijken Persoon objecten op voor- en achternaam:

Persoon natalie = nieuwe persoon ("Natalie", "Portman"); Persoon joe = nieuwe persoon ("Joe", "Portman"); int comparisonResult = ComparisonChain.start () .compare (natalie.getLastName (), joe.getLastName ()) .compare (natalie.getFirstName (), joe.getFirstName ()) .result (); assertThat (comparisonResult) .isPositive ();

De onderliggende vergelijking wordt bereikt met behulp van de vergelijk met() methode, dus de argumenten doorgegeven aan de vergelijken() methoden moeten ofwel primitieven zijn of Vergelijkbaars.

9. Conclusie

In dit artikel hebben we gekeken naar de verschillende manieren om objecten in Java te vergelijken. We onderzochten het verschil tussen gelijkheid, gelijkheid en ordening. We hebben ook gekeken naar de overeenkomstige functies in de Apache Commons- en Guava-bibliotheken.

Zoals gewoonlijk is de volledige code voor dit artikel te vinden op GitHub.