CodeGym /Java blog /Tilfældig /Javas Comparator-grænseflade
John Squirrels
Niveau
San Francisco

Javas Comparator-grænseflade

Udgivet i gruppen
De dovne er ikke de eneste, der skriver om komparatorer og sammenligninger i Java. Jeg er ikke doven, så vær sød at elske og klage over endnu en forklaring. Jeg håber ikke, det bliver overflødigt. Og ja, denne artikel er svaret på spørgsmålet: " Kan du skrive en komparator fra hukommelsen? " Jeg håber, at alle vil være i stand til at skrive en komparator fra hukommelsen efter at have læst denne artikel. Javas Comparator interface - 1

Introduktion

Som du ved, er Java et objektorienteret sprog. Som et resultat er det sædvanligt at manipulere objekter i Java. Men før eller siden står du over for opgaven med at sammenligne objekter baseret på nogle karakteristika. For eksempel : Antag, at vi har en besked beskrevet af Messageklassen:

public static class Message {
    private String message;
    private int id;
        
    public Message(String message) {
        this.message = message;
        this.id = new Random().nextInt(1000);
    }
    public String getMessage() {
        return message;
    }
    public Integer getId() {
        return id;
    }
    public String toString() {
        return "[" + id + "] " + message;
    }
}
Sæt denne klasse i Tutorialspoint Java-kompileren . Glem ikke at tilføje importerklæringerne også:

import java.util.Random;
import java.util.ArrayList;
import java.util.List;
I mainmetoden skal du oprette flere meddelelser:

public static void main(String[] args){
    List<Message> messages = new ArrayList();
    messages.add(new Message("Hello, World!"));
    messages.add(new Message("Hello, Sun!"));
    System.out.println(messages);
}
Lad os tænke på, hvad vi ville gøre, hvis vi ville sammenligne dem? For eksempel vil vi sortere efter id. Og for at skabe en orden, skal vi på en eller anden måde sammenligne objekterne for at forstå, hvilket objekt der skal komme først (dvs. den mindre) og hvilken der skal følge (dvs. den større). Lad os starte med en klasse som java.lang.Object . Vi ved, at alle klasser implicit arver Objectklassen. Og dette giver mening, fordi det afspejler konceptet om, at "alt er et objekt" og giver fælles adfærd for alle klasser. Denne klasse dikterer, at hver klasse har to metoder: → hashCode Metoden hashCodereturnerer nogle numeriske (int) repræsentation af objektet. Hvad betyder det? Det betyder, at hvis du opretter to forskellige forekomster af en klasse, så skal de have forskellige hashCodes. Metodens beskrivelse siger så meget: "Så meget som det er rimeligt praktisk, returnerer hashCode-metoden defineret af klassen Object distinkte heltal for distinkte objekter". Med andre ord, for to forskellige instances skal der være forskellige hashCodes. Det vil sige, at denne metode ikke er egnet til vores sammenligning. → equals. Metoden equalsbesvarer spørgsmålet "er disse objekter ligeværdige?" og returnerer et boolean." Som standard har denne metode følgende kode:

public boolean equals(Object obj) {
    return (this == obj);
}
Det vil sige, at hvis denne metode ikke tilsidesættes, siger den i det væsentlige, om objektreferencerne matcher eller ej. Det er ikke det, vi ønsker for vores beskeder, fordi vi er interesserede i besked-id'er, ikke objektreferencer. Og selvom vi tilsidesætter equalsmetoden, er det mest, vi kan håbe på, at lære, om de er ligeværdige. Og det er ikke nok for os til at bestemme rækkefølgen. Så hvad har vi så brug for? Vi har brug for noget, der kan sammenlignes. Den der sammenligner er en Comparator. Åbn Java API og find Comparator . Der er faktisk en java.util.Comparatorgrænseflade java.util.Comparator and java.util.Comparable Som du kan se, eksisterer en sådan grænseflade. En klasse, der implementerer det, siger: "Jeg implementerer en metode, der sammenligner objekter." Det eneste, du virkelig skal huske, er komparatorkontrakten, som er udtrykt som følger:

Comparator returns an int according to the following rules: 
  • It returns a negative int if the first object is smaller
  • It returns a positive int if the first object is larger
  • It returns zero if the objects are equal
Lad os nu skrive en komparator. Vi bliver nødt til at importere java.util.Comparator. Efter importerklæringen skal du tilføje følgende til metoden main: Comparator<Message> comparator = new Comparator<Message>(); Dette virker selvfølgelig ikke, fordi Comparatordet er en grænseflade. Så vi tilføjer krøllede seler {}efter parentesen. Skriv følgende metode inde i bøjlerne:

public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Du behøver ikke engang at huske stavningen. En komparator er en, der udfører en sammenligning, det vil sige, den sammenligner. For at angive den relative rækkefølge af objekterne returnerer vi en int. Det er i bund og grund det. Dejligt og nemt. Som du kan se fra eksemplet, er der udover Comparator en anden grænseflade — java.lang.Comparable, som kræver, at vi implementerer compareTometoden. Denne grænseflade siger, "en klasse, der implementerer mig, gør det muligt at sammenligne forekomster af klassen." For eksempel Integer's implementering af compareTo er som følger:

(x < y) ? -1 : ((x == y) ? 0 : 1)
Java 8 introducerede nogle gode ændringer. Hvis du ser nærmere på grænsefladen Comparator, vil du se @FunctionalInterfaceannoteringen over den. Denne annotation er til informationsformål og fortæller os, at denne grænseflade er funktionel. Det betyder, at denne grænseflade kun har 1 abstrakt metode, som er en metode uden implementering. Hvad giver det os? Nu kan vi skrive komparatorens kode sådan her:

Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Vi navngiver variablerne i parentes. Java vil se, at fordi der kun er én metode, så er det nødvendige antal og typer af inputparametre klare. Så bruger vi pileoperatoren til at sende dem til denne del af koden. Hvad mere er, takket være Java 8 har vi nu standardmetoder i grænseflader. Disse metoder vises som standard, når vi implementerer en grænseflade. Interfacet Comparatorhar flere. For eksempel:

Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Der er en anden metode, der vil gøre din kode renere. Tag et kig på eksemplet ovenfor, hvor vi definerede vores komparator. Hvad gør den? Det er ret primitivt. Det tager simpelthen et objekt og udtrækker en værdi, der er "sammenlignelig". For eksempel Integerimplementerer comparable, så vi er i stand til at udføre en compareTo-operation på værdierne af meddelelses-id-felter. Denne simple komparatorfunktion kan skrives sådan:

Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Med andre ord har vi en, Comparatorder sammenligner sådan her: den tager objekter, bruger getId()metoden til at få en Comparablefra dem og bruger derefter compareTotil at sammenligne. Og der er ikke flere forfærdelige konstruktioner. Og til sidst vil jeg bemærke endnu en funktion. Komparatorer kan kædes sammen. For eksempel:

Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Ansøgning

At erklære en komparator viser sig at være ret logisk, synes du ikke? Nu skal vi se, hvordan og hvor vi skal bruge det. → Collections.sort(java.util.Collections) Vi kan selvfølgelig sortere samlinger på denne måde. Men ikke hver samling, kun lister. Der er ikke noget usædvanligt her, fordi lister er den slags samlinger, hvor du får adgang til elementer ved deres indeks. Dette gør det muligt at udskifte det andet element med det tredje element. Derfor er følgende sorteringsmetode kun for lister:

Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort(java.util.Arrays) Arrays er også nemme at sortere. Igen, af samme grund - deres elementer er tilgået af indeks. → Descendants of java.util.SortedSet and java.util.SortedMap Du vil huske det Setog Mapgaranterer ikke, i hvilken rækkefølge elementerne er gemt. MEN vi har specielle implementeringer, der garanterer ordren. Og hvis en samlings elementer ikke implementerer java.util.Comparable, så kan vi sende en Comparatortil dens konstruktør:

Set<Message> msgSet = new TreeSet(comparator);
Stream API I Stream API'et, som dukkede op i Java 8, giver komparatorer dig mulighed for at forenkle arbejdet med stream-elementer. Antag for eksempel, at vi har brug for en række af tilfældige tal fra 0 til 999, inklusive:

Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Vi kunne stoppe her, men der er endnu mere interessante problemer. Antag for eksempel, at du skal forberede en Map, hvor nøglen er et meddelelses-id. Derudover ønsker vi at sortere disse nøgler, så vi starter med følgende kode:

Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Vi får faktisk en HashMapher. Og som vi ved, garanterer det ikke nogen orden. Som følge heraf mister vores elementer, som blev sorteret efter id, simpelthen deres rækkefølge. Ikke godt. Vi bliver nødt til at ændre vores samler lidt:

Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg, (oldValue, newValue) -> oldValue, TreeMap::new));
Koden er begyndt at se lidt mere skræmmende ud, men nu er problemet løst korrekt. Læs mere om de forskellige grupperinger her: Du kan oprette din egen samler. Læs mere her: "Oprettelse af en brugerdefineret samler i Java 8" . Og du vil have gavn af at læse diskussionen her: "Java 8-liste til at kortlægge med stream" .

Faldfælde

Comparatorog Comparableer gode. Men der er én nuance, du bør huske. Når en klasse udfører sortering, forventer den, at din klasse kan konverteres til en Comparable. Hvis dette ikke er tilfældet, vil du modtage en fejl under kørsel. Lad os se på et eksempel:

SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Det ser ud til, at der ikke er noget galt her. Men faktisk, i vores eksempel, vil den fejle med en fejl: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Og alt sammen fordi den forsøgte at sortere elementerne (det er SortedSetjo en ), men kunne ikke. Glem ikke dette, når du arbejder med SortedMapog SortedSet.

Mere læsning:

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