CodeGym/Blog Java/Aleatoriu/Interfața Comparator Java
John Squirrels
Nivel
San Francisco

Interfața Comparator Java

Publicat în grup
Leneșii nu sunt singurii care scriu despre Comparatoare și comparații în Java. Nu sunt leneș, așa că vă rugăm să iubiți și să vă reproșați încă o explicație. Sper să nu fie de prisos. Și da, acest articol este răspunsul la întrebarea: „ Poți să scrii un comparator din memorie? ” Sper că toată lumea va putea scrie un comparator din memorie după ce a citit acest articol. Interfața Javas Comparator - 1

Introducere

După cum știți, Java este un limbaj orientat pe obiecte. Ca rezultat, este obișnuit să manipulezi obiecte în Java. Dar, mai devreme sau mai târziu, vă confruntați cu sarcina de a compara obiecte pe baza unor caracteristici. De exemplu : Să presupunem că avem un mesaj descris de Messageclasă:
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;
    }
}
Puneți această clasă în compilatorul Java Tutorialspoint . Nu uitați să adăugați și declarațiile de import:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
În mainmetodă, creați mai multe mesaje:
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);
}
Să ne gândim ce am face dacă am vrea să le comparăm? De exemplu, vrem să sortăm după id. Și pentru a crea o comandă, trebuie să comparăm cumva obiectele pentru a înțelege care obiect ar trebui să vină primul (adică cel mai mic) și care ar trebui să urmeze (adică cel mai mare). Să începem cu o clasă precum java.lang.Object . Știm că toate clasele moștenesc implicit Objectclasa. Și acest lucru are sens deoarece reflectă conceptul că „totul este un obiect” și oferă un comportament comun pentru toate clasele. Această clasă dictează că fiecare clasă are două metode: → hashCode Metoda hashCodereturnează niște valori numerice (int) reprezentarea obiectului. Ce înseamnă asta? Înseamnă că, dacă creați două instanțe diferite ale unei clase, atunci acestea ar trebui să aibă hashCodes-uri diferite. Descrierea metodei spune la fel de mult: „Oricât de mult este practic, metoda hashCode definită de clasa Object returnează numere întregi distincte pentru obiecte distincte”. Cu alte cuvinte, pentru două instances-uri diferite, ar trebui să existe hashCodes-uri diferite. Adică, această metodă nu este potrivită pentru comparația noastră. → equals. Metoda equalsrăspunde la întrebarea „sunt aceste obiecte egale?” și returnează un boolean." În mod implicit, această metodă are următorul cod:
public boolean equals(Object obj) {
    return (this == obj);
}
Adică, dacă această metodă nu este suprascrisă, ea spune în esență dacă referințele la obiect se potrivesc sau nu. Acest lucru nu este ceea ce ne dorim pentru mesajele noastre, pentru că ne interesează ID-urile mesajelor, nu referințele la obiecte. Și chiar dacă trecem peste equalsmetoda, cel mai mult la care putem spera este să aflăm dacă sunt egali. Și acest lucru nu este suficient pentru a stabili ordinea. Deci de ce avem nevoie atunci? Avem nevoie de ceva care să se compare. Cel care compară este un Comparator. Deschideți API-ul Java și găsiți Comparator . Într-adevăr, există o java.util.Comparatorinterfață java.util.Comparator and java.util.Comparable După cum puteți vedea, o astfel de interfață există. O clasă care o implementează spune: „Implementez o metodă care compară obiecte”. Singurul lucru pe care trebuie să-l rețineți este contractul de comparație, care este exprimat după cum urmează:
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
Acum să scriem un comparator. Va trebui să importăm java.util.Comparator. După instrucțiunea de import, adăugați următoarele metode main: Comparator<Message> comparator = new Comparator<Message>(); Desigur, aceasta nu va funcționa, deoarece Comparatoreste o interfață. Așa că adăugăm acolade {}după paranteze. Scrieți următoarea metodă în interiorul acoladelor:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Nici măcar nu trebuie să vă amintiți ortografia. Un comparator este cel care realizează o comparație, adică compară. Pentru a indica ordinea relativă a obiectelor, returnăm un int. Practic asta este. Simplu și ușor. După cum puteți vedea din exemplu, pe lângă Comparator, există o altă interfață — java.lang.Comparable, care ne cere să implementăm compareTometoda. Această interfață spune, „o clasă care mă implementează face posibilă compararea instanțelor clasei”. De exemplu, Integerimplementarea lui compareTo este următoarea:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Java 8 a introdus câteva modificări frumoase. Dacă aruncați o privire mai atentă la Comparatorinterfață, veți vedea @FunctionalInterfaceadnotarea deasupra acesteia. Această adnotare are scop informativ și ne spune că această interfață este funcțională. Aceasta înseamnă că această interfață are doar 1 metodă abstractă, care este o metodă fără implementare. Ce ne oferă asta? Acum putem scrie codul comparatorului astfel:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Numim variabilele în paranteze. Java va vedea că, deoarece există o singură metodă, atunci numărul necesar și tipurile de parametri de intrare sunt clare. Apoi folosim operatorul săgeată pentru a le transmite acestei părți a codului. Mai mult, datorită Java 8, acum avem metode implicite în interfețe. Aceste metode apar implicit atunci când implementăm o interfață. Interfața Comparatorare mai multe. De exemplu:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Există o altă metodă care vă va face codul mai curat. Aruncă o privire la exemplul de mai sus, unde am definit comparatorul nostru. Ce face? Este destul de primitiv. Pur și simplu ia un obiect și extrage o valoare care este „comparabilă”. De exemplu, Integerimplementează comparable, astfel încât să putem efectua o operație compareTo pe valorile câmpurilor de ID mesaj. Această funcție simplă de comparare poate fi scrisă astfel:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Cu alte cuvinte, avem un Comparatorcare se compară astfel: ia obiecte, folosește getId()metoda pentru a obține o Comparablede la ele și apoi folosește compareTopentru a compara. Și nu există construcții mai oribile. Și, în sfârșit, vreau să notez încă o caracteristică. Comparatoarele pot fi înlănțuite. De exemplu:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Aplicație

Declararea unui comparator se dovedește a fi destul de logic, nu crezi? Acum trebuie să vedem cum și unde să-l folosim. → Collections.sort(java.util.Collections) Desigur, putem sorta colecțiile în acest fel. Dar nu orice colecție, doar liste. Nu este nimic neobișnuit aici, deoarece listele sunt genul de colecții în care accesați elemente prin indexul lor. Acest lucru permite ca al doilea element să fie schimbat cu al treilea element. De aceea, următoarea metodă de sortare este doar pentru liste:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort(java.util.Arrays) Matricele sunt, de asemenea, ușor de sortat. Din nou, din același motiv - elementele lor sunt accesate prin index. → Descendants of java.util.SortedSet and java.util.SortedMap Vă veți aminti asta Setși Mapnu garantați ordinea în care sunt stocate elementele. DAR, avem implementări speciale care garantează comanda. Și dacă elementele unei colecții nu implementează java.util.Comparable, atunci putem transmite un Comparatorconstructorului său:
Set<Message> msgSet = new TreeSet(comparator);
Stream API În Stream API, care a apărut în Java 8, comparatoarele vă permit să simplificați lucrul cu elementele fluxului. De exemplu, să presupunem că avem nevoie de o secvență de numere aleatoare de la 0 la 999, inclusiv:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Ne-am putea opri aici, dar sunt și mai multe probleme interesante. De exemplu, să presupunem că trebuie să pregătiți un Map, unde cheia este un ID de mesaj. În plus, dorim să sortăm aceste chei, așa că vom începe cu următorul cod:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
De fapt, primim HashMapaici. Și după cum știm, nu garantează nicio comandă. Ca urmare, elementele noastre, care au fost sortate după id, își pierd pur și simplu ordinea. Nu e bun. Va trebui să ne schimbăm puțin colecționarul:
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));
Codul a început să arate puțin mai înfricoșător, dar acum problema este rezolvată corect. Citiți mai multe despre diferitele grupări aici: Îți poți crea propriul colecționar. Citiți mai multe aici: „Crearea unui colector personalizat în Java 8” . Și vei beneficia de citirea discuției de aici: „Java 8 list to map with stream” .

Cădere-capcană

Comparatorsi Comparablesunt bune. Dar există o nuanță de care ar trebui să-ți amintești. Când o clasă efectuează sortarea, se așteaptă ca clasa dvs. să poată fi convertită într-un Comparable. Dacă nu este cazul, atunci veți primi o eroare în timpul execuției. Să ne uităm la un exemplu:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Se pare că nu este nimic în neregulă aici. Dar, de fapt, în exemplul nostru, va eșua cu o eroare: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Și totul pentru că a încercat să sorteze elementele (este un SortedSet, la urma urmei)...dar nu a putut. Nu uitați acest lucru când lucrați cu SortedMapși SortedSet.

Mai multe lecturi:

Comentarii
  • Popular
  • Nou
  • Vechi
Trebuie să fii conectat pentru a lăsa un comentariu
Această pagină nu are încă niciun comentariu