CodeGym /Java-blogg /Tilfeldig /Javas Comparator-grensesnitt
John Squirrels
Nivå
San Francisco

Javas Comparator-grensesnitt

Publisert i gruppen
De late er ikke de eneste som skriver om komparatorer og sammenligninger i Java. Jeg er ikke lat, så vær så snill å elsk og grip om enda en forklaring. Jeg håper det ikke blir overflødig. Og ja, denne artikkelen er svaret på spørsmålet: " Kan du skrive en komparator fra hukommelsen? " Jeg håper alle vil være i stand til å skrive en komparator fra hukommelsen etter å ha lest denne artikkelen. Javas Comparator-grensesnitt - 1

Introduksjon

Som du vet, er Java et objektorientert språk. Som et resultat er det vanlig å manipulere objekter i Java. Men før eller siden står du overfor oppgaven med å sammenligne objekter basert på en eller annen egenskap. For eksempel : Anta at vi har en melding beskrevet av 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;
    }
}
Sett denne klassen i Tutorialspoint Java-kompilatoren . Ikke glem å legge til importsetningene også:

import java.util.Random;
import java.util.ArrayList;
import java.util.List;
I mainmetoden oppretter du flere meldinger:

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);
}
La oss tenke på hva vi ville gjort hvis vi ville sammenligne dem? Vi ønsker for eksempel å sortere etter id. Og for å skape en rekkefølge, må vi på en eller annen måte sammenligne objektene for å forstå hvilket objekt som skal komme først (dvs. den minste) og hvilken som skal følge (dvs. den større). La oss starte med en klasse som java.lang.Object . Vi vet at alle klasser implisitt arver Objectklassen. Og dette gir mening fordi det reflekterer konseptet om at "alt er et objekt" og gir felles oppførsel for alle klasser. Denne klassen tilsier at hver klasse har to metoder: → hashCode Metoden hashCodereturnerer noen numeriske (int) representasjon av objektet. Hva betyr det? Det betyr at hvis du oppretter to forskjellige forekomster av en klasse, bør de ha forskjellige hashCodes. Metodens beskrivelse sier så mye: "Så mye som det er rimelig praktisk, returnerer hashCode-metoden definert av klassen Object distinkte heltall for distinkte objekter". Med andre ord, for to forskjellige instances, bør det være forskjellige hashCodes. Det vil si at denne metoden ikke er egnet for vår sammenligning. → equals. Metoden equalssvarer på spørsmålet "er disse objektene like?" og returnerer en boolean." Som standard har denne metoden følgende kode:

public boolean equals(Object obj) {
    return (this == obj);
}
Det vil si at hvis denne metoden ikke overstyres, sier den i hovedsak om objektreferansene samsvarer eller ikke. Dette er ikke hva vi ønsker for meldingene våre, fordi vi er interessert i meldings-IDer, ikke objektreferanser. Og selv om vi overstyrer equalsmetoden, er det mest vi kan håpe på å lære om de er likeverdige. Og dette er ikke nok for oss til å bestemme rekkefølgen. Så hva trenger vi da? Vi trenger noe som kan sammenlignes. Den som sammenligner er en Comparator. Åpne Java API og finn Comparator . Det er faktisk et java.util.Comparatorgrensesnitt java.util.Comparator and java.util.Comparable Som du kan se, eksisterer et slikt grensesnitt. En klasse som implementerer den sier: "Jeg implementerer en metode som sammenligner objekter." Det eneste du virkelig trenger å huske er komparatorkontrakten, som er uttrykt 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
La oss nå skrive en komparator. Vi må importere java.util.Comparator. Etter importsetningen legger du til følgende i metoden main: Comparator<Message> comparator = new Comparator<Message>(); Dette vil selvfølgelig ikke fungere, fordi Comparatordet er et grensesnitt. Så vi legger til krøllete tannregulering {}etter parentesen. Skriv følgende metode inne i tannreguleringen:

public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Du trenger ikke engang å huske stavemåten. En komparator er en som utfører en sammenligning, det vil si at den sammenligner. For å indikere den relative rekkefølgen til objektene returnerer vi en int. Det er i grunnen det. Fint og enkelt. Som du kan se fra eksempelet, i tillegg til Comparator, er det et annet grensesnitt — java.lang.Comparable, som krever at vi implementerer compareTometoden. Dette grensesnittet sier, "en klasse som implementerer meg gjør det mulig å sammenligne forekomster av klassen." For eksempel er Integerimplementeringen av compareTo som følger:

(x < y) ? -1 : ((x == y) ? 0 : 1)
Java 8 introduserte noen fine endringer. Hvis du ser nærmere på grensesnittet Comparator, vil du se @FunctionalInterfacemerknaden over det. Denne merknaden er til informasjonsformål og forteller oss at dette grensesnittet er funksjonelt. Dette betyr at dette grensesnittet kun har 1 abstrakt metode, som er en metode uten implementering. Hva gir dette oss? Nå kan vi skrive komparatorens kode slik:

Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Vi navngir variablene i parentes. Java vil se at fordi det bare er én metode, er det nødvendige antallet og typene inndataparametere klare. Deretter bruker vi piloperatoren for å sende dem til denne delen av koden. Dessuten, takket være Java 8, har vi nå standardmetoder i grensesnitt. Disse metodene vises som standard når vi implementerer et grensesnitt. Grensesnittet Comparatorhar flere. For eksempel:

Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Det er en annen metode som vil gjøre koden renere. Ta en titt på eksempelet ovenfor, der vi definerte komparatoren vår. Hva gjør den? Det er ganske primitivt. Det tar ganske enkelt et objekt og trekker ut en verdi som er "sammenlignbar". For eksempel Integerimplementerer comparable, slik at vi kan utføre en compareTo-operasjon på verdiene til meldings-ID-felt. Denne enkle komparatorfunksjonen kan skrives slik:

Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Med andre ord, vi har en Comparatorsom sammenligner slik: den tar objekter, bruker getId()metoden for å få en Comparablefra dem, og bruker deretter compareTotil å sammenligne. Og det er ingen flere forferdelige konstruksjoner. Og til slutt vil jeg merke meg en funksjon til. Komparatorer kan lenkes. For eksempel:

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

applikasjon

Å erklære en komparator viser seg å være ganske logisk, synes du ikke? Nå må vi se hvordan og hvor vi skal bruke det. → Collections.sort(java.util.Collections) Vi kan selvfølgelig sortere samlinger på denne måten. Men ikke hver samling, bare lister. Det er ikke noe uvanlig her, fordi lister er den typen samlinger hvor du får tilgang til elementer ved hjelp av deres indeks. Dette gjør at det andre elementet kan byttes med det tredje elementet. 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) Matriser er også enkle å sortere. Igjen, av samme grunn - elementene deres er tilgjengelig via indeks. → Descendants of java.util.SortedSet and java.util.SortedMap Du vil huske det Setog Mapgaranterer ikke rekkefølgen elementene lagres i. MEN, vi har spesielle implementeringer som garanterer bestillingen. Og hvis en samlings elementer ikke implementerer java.util.Comparable, kan vi sende en Comparatortil konstruktøren:

Set<Message> msgSet = new TreeSet(comparator);
Stream API I Stream API, som dukket opp i Java 8, lar komparatorer deg forenkle arbeidet med strømelementer. Anta for eksempel at vi trenger en sekvens med tilfeldige tall fra 0 til 999, inkludert:

Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Vi kunne stoppet her, men det er enda mer interessante problemer. Anta for eksempel at du må forberede en Map, hvor nøkkelen er en meldings-ID. I tillegg ønsker vi å sortere disse nøklene, 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 vet, garanterer det ingen rekkefølge. Som et resultat mister elementene våre, som ble sortert etter id, rett og slett rekkefølgen. Ikke bra. Vi må endre litt på samleren vår:

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 har begynt å se litt skumlere ut, men nå er problemet løst riktig. Les mer om de ulike grupperingene her: Du kan lage din egen samler. Les mer her: "Opprette en tilpasset samler i Java 8" . Og du vil ha nytte av å lese diskusjonen her: "Java 8-liste for å kartlegge med strøm" .

Fall-felle

Comparatorog Comparableer gode. Men det er én nyanse du bør huske. Når en klasse utfører sortering, forventer den at klassen din kan konverteres til en Comparable. Hvis dette ikke er tilfelle, vil du motta en feilmelding under kjøring. La oss se på et eksempel:

SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Det ser ut til at ingenting er galt her. Men faktisk, i vårt eksempel, vil den mislykkes med en feil: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Og alt fordi den prøvde å sortere elementene (det er SortedSettross alt en )...men klarte det ikke. Ikke glem dette når du jobber med SortedMapog SortedSet.
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION