JavaRush /Java Blog /Random-TL /Comparator sa Java

Comparator sa Java

Nai-publish sa grupo
Tanging mga tamad na tao ang hindi sumulat tungkol sa Comparator at paghahambing sa Java. Hindi ako tamad - kaya hinihiling ko sa iyo na mahalin at paboran ang isa pang pagkakaiba-iba. Sana hindi ito kalabisan. At oo, ang artikulong ito ay ang sagot sa tanong: "Maaari ka bang magsulat ng isang comparator mula sa memorya?" Umaasa ako na pagkatapos basahin ang artikulong ito lahat ay makakasulat ng isang comparator mula sa memorya.
Comparator sa Java - 1
Panimula Ang Java ay kilala bilang isang object-oriented na wika. Bilang isang resulta, sa Java ay karaniwan na gumana sa mga bagay. Ngunit maaga o huli ang gawain ng paghahambing ng mga bagay ayon sa ilang prinsipyo ay lumitaw. Kaya, ibinigay: Mayroon kaming ilang mensahe, na inilarawan ng klase ng Mensahe:
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;
    }
}
Idagdag natin ang klase na ito sa Tutorialspoint java compiler . Tandaan din nating magdagdag ng mga import:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
Sa pangunahing pamamaraan gagawa kami ng ilang mga mensahe:
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);
}
Pag-isipan natin kung ano ang dapat nating gawin kung gusto natin silang ikumpara? Halimbawa, gusto naming ayusin ayon sa id. At upang lumikha ng pagkakasunud-sunod, kailangan mong ihambing ang mga bagay upang maunawaan kung aling bagay ang nauna (iyon ay, mas maliit) at kung alin ang susunod (iyon ay, mas malaki). Magsimula tayo sa isang klase tulad ng java.lang.Object . Tulad ng alam natin, ang lahat ng mga klase ay nagmana nang tahasan mula sa klase ng Bagay na ito. At ito ay lohikal, dahil Ito ay mahalagang nagpapahayag ng konsepto: "Lahat ay isang bagay" at nagbibigay ng karaniwang pag-uugali para sa lahat ng mga klase. At tinukoy ng klase na ito na ang bawat klase ay may dalawang pamamaraan: → hashCode Ang pamamaraan ng hashCode ay nagbabalik ng ilang numeric (int) na representasyon ng bagay bilang isang halimbawa ng klase. Ano ang ibig sabihin nito? Nangangahulugan ito na kung gumawa ka ng dalawang magkaibang instance ng isang klase, dahil magkaiba ang mga instance, dapat iba ang hashCode nila. Ito ang sinasabi nito sa paglalarawan ng pamamaraan: "Hangga't makatwirang praktikal, ang pamamaraan ng hashCode na tinukoy ng Class Object ay nagbabalik ng mga natatanging integer para sa mga natatanging bagay" Iyon ay, kung ito ay dalawang magkaibang mga pagkakataon, dapat silang magkaroon ng magkaibang Mga hashCode. Iyon ay, ang pamamaraang ito ay hindi angkop para sa aming paghahambing. → equals Sinasagot ng equals method ang tanong na “are objects equal” at nagbabalik ng boolean. Ang pamamaraang ito ay may default na code:
public boolean equals(Object obj) {
    return (this == obj);
}
Iyon ay, nang hindi na-override ang pamamaraang ito sa isang bagay, mahalagang sinasabi ng pamamaraang ito kung ang mga sanggunian sa bagay ay tumutugma o hindi. Hindi ito angkop para sa aming mga mensahe, dahil hindi kami interesado sa mga link sa bagay, interesado kami sa message id. At kahit na i-override natin ang equals method, ang maximum na makukuha natin ay: “Sila ay pantay-pantay” o “Sila ay hindi pantay-pantay.” Ngunit ito ay hindi sapat para sa amin upang matukoy ang pagkakasunud-sunod.

Comparator at Comparable sa Java

Ano ang nababagay sa atin? Kung isasalin natin ang salitang "compare" sa English sa translator, makukuha natin ang translation na "compare". Mahusay, kung gayon kailangan natin ng isang taong magkukumpara. Kung ikukumpara mo itong paghahambing, kung gayon ang nagkukumpara ay ang Kumpare. Buksan natin ang Java Api at hanapin ang Comparator doon . At sa katunayan, mayroong ganoong interface - java.util.Comparator java.util.Comparator at java.lang.Comparable Gaya ng nakikita mo, mayroong ganoong interface. Sinasabi ng klase na nagpapatupad nito na "Nagpapatupad ako ng isang function para sa paghahambing ng mga bagay." Ang tanging bagay na talagang dapat tandaan ay ang kontrata ng paghahambing, na ipinahayag tulad ng sumusunod:

Comparator возвращает int по следующей схеме: 
  • отрицательный int (первый an object отрицательный, то есть меньше)
  • положительный int (первый an object положительный, хороший, то есть больший)
  • ноль = an objectы равны
Ngayon magsulat tayo ng isang comparator. Kakailanganin naming mag-import ng java.util.Comparator . Pagkatapos ng pag-import, magdagdag ng isang paraan sa pangunahing: Comparator<Message> comparator = new Comparator<Message>(); Naturally, hindi ito gagana, dahil Ang comparator ay isang interface. Samakatuwid, pagkatapos ng mga panaklong magdaragdag kami ng mga kulot { }. Sa mga bracket na ito isusulat namin ang pamamaraan:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Hindi mo na kailangang tandaan na isulat ito. Ang isang comparator ay isa na gumaganap ng isang paghahambing, iyon ay, gumagawa ng isang paghahambing. Upang masagot ang tanong kung anong pagkakasunud-sunod ng mga pinaghahambing na bagay, ibabalik namin ang int. Iyon lang, actually. Simple at madali. Tulad ng nakikita natin mula sa halimbawa, bilang karagdagan sa Comparator, mayroong isa pang interface - java.lang.Comparable , na ipinapatupad na dapat nating tukuyin ang compareTo method . Sinasabi ng interface na ito na "Ang isang klase na nagpapatupad ng isang interface ay nagpapahintulot sa mga pagkakataon ng klase na maihambing." Halimbawa, ang pagpapatupad ng Integer ng compareTo ay ganito ang hitsura:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Paano matandaan ang lahat ng mga interface na ito? Para saan? Lahat ay galing sa English. Paghambingin - upang ihambing, ang nagkukumpara ay Tagapaghambing (bilang isang rehistro, halimbawa. Ibig sabihin, ang nagrerehistro), at ang pang-uri na "ipinahambing" ay Naihahambing. Well, "Ihambing sa" ay isinalin hindi lamang bilang paghahambing sa, ngunit din bilang paghahambing sa. Simple lang. Ang wikang Java ay isinulat ng mga taong nagsasalita ng Ingles, at sa pagbibigay ng pangalan sa lahat ng bagay sa Java sila ay ginagabayan lamang ng Ingles at mayroong ilang uri ng lohika sa pagbibigay ng pangalan. At ang paraan ng compareTo ay naglalarawan kung paano dapat ihambing ang isang instance ng isang klase sa iba pang mga instance. Halimbawa, ang mga string ay inihahambing sa leksigrapiko , at ang mga numero ay inihahambing ayon sa halaga.
Comparator sa Java - 2
Nagdala ang Java 8 ng ilang magagandang pagbabago. Kung titingnan nating mabuti ang interface ng Comparator, makikita natin na mayroong annotation sa itaas nito @FunctionalInterface. Sa katunayan, ang anotasyong ito ay para sa impormasyon at nangangahulugan na gumagana ang interface na ito. Nangangahulugan ito na ang interface na ito ay mayroon lamang 1 abstract na pamamaraan na walang pagpapatupad. Ano ang ibinibigay nito sa atin? Maaari naming isulat ang comparator code ngayon tulad nito:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Sa panaklong ay kung paano namin pinangalanan ang mga variable. Ang Java mismo ay makikita iyon dahil. Kung mayroon lamang isang paraan, kung gayon malinaw kung anong mga parameter ng input ang kailangan, ilan, at anong mga uri. Susunod, sinasabi namin gamit ang isang arrow na gusto naming ilipat ang mga ito sa seksyong ito ng code. Bilang karagdagan, salamat sa Java 8, ang mga default na pamamaraan ay lumitaw sa mga interface - ito ay mga pamamaraan na lumilitaw bilang default (bilang default) kapag nagpapatupad kami ng isang interface. Mayroong ilan sa mga ito sa interface ng Comparator. Halimbawa:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
May isa pang paraan na gagawing mas malinis ang iyong code. Tingnan natin ang halimbawa sa itaas, kung saan inilarawan namin ang aming comparator. Ano ang ginagawa niya? Ito ay medyo primitive. Ito ay kumukuha lamang ng isang bagay at kumukuha ng ilang halaga mula dito na maihahambing. Halimbawa, ang Integer ay nagpapatupad ng maihahambing, kaya nagawa naming magsagawa ng compareTo sa mga halaga ng message id. Ang simpleng comparator function na ito ay maaari ding isulat ng ganito:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Iyon ay, literal, "Mayroon kaming Comparator na naghahambing ng ganito: ito ay tumatagal ng mga bagay, nakakakuha ng Comparable mula sa kanila gamit ang getId() na pamamaraan, naghahambing gamit ang compareTo." At wala nang mga kahila-hilakbot na disenyo. At sa wakas, nais kong tandaan ang isa pang tampok. Maaaring magkadena ang mga kumpare. Halimbawa:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Aplikasyon

Ang deklarasyon ng comparator ay naging medyo lohikal, hindi ba? Ngayon ay kailangan nating makita kung paano ito gamitin at sa anong mga lugar. → Collections.sort (java.util.Collections) Siyempre, maaari nating ayusin ang mga koleksyon sa ganitong paraan. Pero hindi lahat, listahan lang. At walang kakaiba dito, dahil... Ito ang listahan na nangangailangan ng access sa isang elemento ayon sa index. At ito ay nagpapahintulot sa element number two na mapalitan ng element number three. Samakatuwid, ang pag-uuri sa ganitong paraan ay posible lamang para sa mga listahan:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort (java.util.Arrays) Ang mga array ay maginhawa din upang pagbukud-bukurin. Muli, para sa parehong dahilan ng pag-access ng mga elemento sa pamamagitan ng index. → Mga inapo ng java.util.SortedSet at java.util.SortedMap Gaya ng naaalala namin, hindi ginagarantiya ng Set at Map ang pagkakasunud-sunod ng pag-iimbak ng mga tala. PERO mayroon kaming mga espesyal na pagpapatupad na ginagarantiyahan ang kaayusan. At kung ang mga elemento ng koleksyon ay hindi nagpapatupad ng java.lang.Comparable, maaari nating ipasa ang Comparator sa constructor ng naturang mga koleksyon:
Set<Message> msgSet = new TreeSet(comparator);
Stream API Sa Stream Api, na lumabas sa Java 8, pinapayagan ka ng isang comparator na pasimplehin ang trabaho sa mga elemento ng stream. Halimbawa, kailangan namin ng pagkakasunod-sunod ng mga random na numero mula 0 hanggang 999 kasama:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Maaari kaming huminto, ngunit may mas kawili-wiling mga problema. Halimbawa, kailangan mong maghanda ng Map, kung saan ang susi ay ang message id. Kasabay nito, gusto naming pag-uri-uriin ang mga key na ito upang ang mga susi ay maayos, mula sa pinakamaliit hanggang sa pinakamalaki. Magsimula tayo sa code na ito:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Ang babalikan natin dito ay talagang isang HashMap. At tulad ng alam natin, hindi nito ginagarantiyahan ang anumang order. Samakatuwid, ang aming mga talaan na pinagsunod-sunod ayon sa ID ay nawala sa ayos. Hindi maganda. Kailangan nating palitan ng kaunti ang ating kolektor:
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));
Ang code ay mukhang mas katakut-takot, ngunit ang problema ay nalutas na ngayon nang tama salamat sa tahasang pagpapatupad ng TreeMap. Maaari mong basahin ang higit pa tungkol sa iba't ibang mga grupo dito: Maaari kang lumikha ng kolektor sa iyong sarili. Maaari kang magbasa nang higit pa dito: "Paglikha ng isang pasadyang kolektor sa Java 8" . At kapaki-pakinabang na basahin ang talakayan dito: "Java 8 list to map with stream" .
Comparator sa Java - 3
Magaling ang Comparator at Comparable rakes . Ngunit mayroong isang nuance na nauugnay sa kanila na nagkakahalaga ng pag-alala. Kapag nagsagawa ng pag-uuri ang isang klase, kinakalkula nito na maaari nitong i-cast ang iyong klase sa Comparable. Kung hindi ito ang kaso, makakatanggap ka ng error sa oras ng pagpapatupad. Tingnan natin ang isang halimbawa:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Parang walang mali dito. Ngunit sa katunayan, sa aming halimbawa, ito ay mag-crash na may error: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable At lahat dahil sinubukan nitong ayusin ang mga elemento (Ito ay isang SortedSet, pagkatapos ng lahat). At hindi ko kaya. Dapat mong tandaan ito kapag nagtatrabaho sa SortedMap at SortedSet. Karagdagang Inirerekomenda para sa pagtingin: Yuri Tkach: HashSet at TreeSet - Mga Koleksyon #1 - Advanced na Java
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION