CodeGym /Java Blog /Random /Ang interface ng Comparator ng Java
John Squirrels
Antas
San Francisco

Ang interface ng Comparator ng Java

Nai-publish sa grupo
Ang mga tamad ay hindi lamang ang magsulat tungkol sa mga Comparator at paghahambing sa Java. Hindi ako tamad, kaya mangyaring mahalin at magalit tungkol sa isa pang paliwanag. 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 lahat ay makakasulat ng isang comparator mula sa memorya pagkatapos basahin ang artikulong ito. Javas Comparator interface - 1

Panimula

Tulad ng alam mo, ang Java ay isang object-oriented na wika. Bilang resulta, kaugalian na manipulahin ang mga bagay sa Java. Ngunit maaga o huli, haharapin mo ang gawain ng paghahambing ng mga bagay batay sa ilang katangian. Halimbawa : Ipagpalagay na mayroon kaming ilang mensahe na inilarawan ng Messageklase:

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;
    }
}
Ilagay ang klase na ito sa Tutorialspoint Java compiler . Huwag kalimutang idagdag din ang mga pahayag ng pag-import:

import java.util.Random;
import java.util.ArrayList;
import java.util.List;
Sa mainpamamaraan, lumikha 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 gagawin natin kung gusto nating ikumpara ang mga ito? Halimbawa, gusto naming ayusin ayon sa id. At upang lumikha ng isang pagkakasunud-sunod, kailangan nating ihambing ang mga bagay sa anumang paraan upang maunawaan kung aling bagay ang dapat mauna (ibig sabihin, ang mas maliit) at kung alin ang dapat sundin (ibig sabihin, ang mas malaki). Magsimula tayo sa isang klase tulad ng java.lang.Object . Alam namin na ang lahat ng mga klase ay tahasang nagmamana ng Objectklase. At ito ay makatuwiran dahil ito ay sumasalamin sa konsepto na "lahat ay isang bagay" at nagbibigay ng karaniwang pag-uugali para sa lahat ng mga klase. Ang klase na ito ay nagdidikta na ang bawat klase ay may dalawang pamamaraan: → hashCode Ang hashCodepamamaraan ay nagbabalik ng ilang numeric (int) representasyon ng bagay. Anong ibig sabihin niyan? Nangangahulugan ito na kung lumikha ka ng dalawang magkaibang mga pagkakataon ng isang klase, dapat silang magkaroon ng magkaibang hashCodes. Ang paglalarawan ng pamamaraan ay nagsasabing: "Hangga't makatwirang praktikal, ang pamamaraan ng hashCode na tinukoy ng Class Object ay nagbabalik ng mga natatanging integer para sa mga natatanging bagay". Sa madaling salita, para sa dalawang magkaibang instances, dapat mayroong magkaibang hashCodes. Iyon ay, ang pamamaraang ito ay hindi angkop para sa aming paghahambing. → equals. equalsSinasagot ng pamamaraan ang tanong na "pantay ba ang mga bagay na ito?" at nagbabalik ng boolean." Bilang default, ang pamamaraang ito ay may sumusunod na code:

public boolean equals(Object obj) {
    return (this == obj);
}
Iyon ay, kung ang pamamaraang ito ay hindi na-override, mahalagang sinasabi nito kung ang mga sanggunian ng bagay ay tumutugma o hindi. Hindi ito ang gusto namin para sa aming mga mensahe, dahil interesado kami sa mga message id, hindi sa mga object reference. At kahit na i-override natin ang equalspamamaraan, ang pinaka-maaasahan natin ay upang malaman kung sila ay pantay. At ito ay hindi sapat para sa amin upang matukoy ang pagkakasunud-sunod. Kaya ano ang kailangan natin? Kailangan namin ng isang bagay na naghahambing. Ang nagkukumpara ay isang Comparator. Buksan ang Java API at hanapin ang Comparator . Sa katunayan, mayroong isang java.util.Comparatorinterface java.util.Comparator and java.util.Comparable Tulad ng nakikita mo, umiiral ang gayong interface. Ang isang klase na nagpapatupad nito ay nagsasabing, "Nagpapatupad ako ng isang pamamaraan na naghahambing ng mga bagay." Ang tanging bagay na talagang kailangan mong tandaan ay ang kontrata ng paghahambing, na ipinahayag tulad ng sumusunod:

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
Ngayon magsulat tayo ng isang comparator. Kakailanganin nating mag-import java.util.Comparator. Pagkatapos ng pahayag ng pag-import, idagdag ang sumusunod sa mainpamamaraan: Comparator<Message> comparator = new Comparator<Message>(); Siyempre, hindi ito gagana, dahil ito Comparatoray isang interface. Kaya nagdaragdag kami ng mga kulot na braces {}pagkatapos ng mga panaklong. Isulat ang sumusunod na paraan sa loob ng braces:

public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Hindi mo na kailangan pang tandaan ang spelling. Ang comparator ay isa na nagsasagawa ng paghahambing, iyon ay, ito ay naghahambing. Upang ipahiwatig ang kamag-anak na pagkakasunud-sunod ng mga bagay, ibinabalik namin ang isang int. Iyon talaga. Maganda at madali. Tulad ng makikita mo mula sa halimbawa, bilang karagdagan sa Comparator, mayroong isa pang interface — java.lang.Comparable, na nangangailangan sa amin na ipatupad ang compareTopamamaraan. Ang interface na ito ay nagsasabing, "isang klase na nagpapatupad sa akin ay ginagawang posible na ihambing ang mga pagkakataon ng klase." Halimbawa, Integerang pagpapatupad ng compareTo ay ang mga sumusunod:

(x < y) ? -1 : ((x == y) ? 0 : 1)
Ipinakilala ng Java 8 ang ilang magagandang pagbabago. Kung titingnan mo nang mabuti ang Comparatorinterface, makikita mo ang @FunctionalInterfaceanotasyon sa itaas nito. Ang anotasyong ito ay para sa mga layunin ng impormasyon at sinasabi sa amin na gumagana ang interface na ito. Nangangahulugan ito na ang interface na ito ay may 1 abstract na pamamaraan lamang, na isang paraan na walang pagpapatupad. Ano ang ibinibigay nito sa atin? Ngayon ay maaari na nating isulat ang code ng comparator tulad nito:

Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Pinangalanan namin ang mga variable sa panaklong. Makikita iyon ng Java dahil mayroon lamang isang paraan, kung gayon ang kinakailangang numero at mga uri ng mga parameter ng pag-input ay malinaw. Pagkatapos ay ginagamit namin ang arrow operator upang ipasa ang mga ito sa bahaging ito ng code. Higit pa rito, salamat sa Java 8, mayroon na kaming mga default na pamamaraan sa mga interface. Lumilitaw ang mga pamamaraang ito bilang default kapag nagpatupad kami ng interface. Ang Comparatorinterface ay may ilang. Halimbawa:

Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
May isa pang paraan na gagawing mas malinis ang iyong code. Tingnan ang halimbawa sa itaas, kung saan tinukoy namin ang aming comparator. Ano ang ginagawa nito? Ito ay medyo primitive. Ito ay kumukuha lamang ng isang bagay at kumukuha ng ilang halaga na "maihahambing". Halimbawa, Integeripinapatupad ang comparable, para makapagsagawa kami ng operasyong compareTo sa mga halaga ng mga field ng message id. Ang simpleng comparator function na ito ay maaaring isulat tulad nito:

Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Sa madaling salita, mayroon kaming isang Comparatornaghahambing na tulad nito: nangangailangan ito ng mga bagay, ginagamit ang getId()pamamaraan upang makakuha ng Comparablemula sa kanila, at pagkatapos ay ginagamit compareToupang ihambing. At wala nang mga kakila-kilabot na konstruksyon. At sa wakas, gusto kong tandaan ang isa pang tampok. Maaaring ikadena ang mga kumpare. Halimbawa:

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

Aplikasyon

Ang pagdedeklara ng isang comparator ay lumalabas na medyo lohikal, hindi ba? Ngayon kailangan nating makita kung paano at saan ito gagamitin. → Collections.sort(java.util.Collections) Maaari naming, siyempre, pag-uri-uriin ang mga koleksyon sa ganitong paraan. Ngunit hindi lahat ng koleksyon, mga listahan lamang. Walang kakaiba dito, dahil ang mga listahan ay ang uri ng mga koleksyon kung saan ina-access mo ang mga elemento ayon sa kanilang index. Ito ay nagpapahintulot sa pangalawang elemento na mapalitan ng ikatlong elemento. Kaya naman ang sumusunod na paraan ng pag-uuri ay para lamang sa mga listahan:

Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort(java.util.Arrays) Madali ring ayusin ang mga array. Muli, para sa parehong dahilan - ang kanilang mga elemento ay na-access sa pamamagitan ng index. → Descendants of java.util.SortedSet and java.util.SortedMap Maaalala mo iyon Setat Maphindi mo ginagarantiya ang pagkakasunud-sunod kung saan nakaimbak ang mga elemento. PERO, mayroon kaming mga espesyal na pagpapatupad na ginagarantiyahan ang order. At kung ang mga elemento ng isang koleksyon ay hindi nagpapatupad ng java.util.Comparable, maaari nating ipasa ang isang Comparatorsa constructor nito:

Set<Message> msgSet = new TreeSet(comparator);
Stream API Sa Stream API, na lumabas sa Java 8, hinahayaan ka ng mga comparator na pasimplehin ang trabaho sa mga elemento ng stream. Halimbawa, ipagpalagay na kailangan namin ng isang pagkakasunud-sunod ng mga random na numero mula 0 hanggang 999, kasama ang:

Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Maaari tayong tumigil dito, ngunit may mga mas kawili-wiling problema. Halimbawa, ipagpalagay na kailangan mong maghanda ng Map, kung saan ang susi ay isang message id. Bukod pa rito, gusto naming ayusin ang mga key na ito, kaya magsisimula kami sa sumusunod na code:

Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Talagang nakakakuha kami HashMapdito. At tulad ng alam namin, hindi nito ginagarantiyahan ang anumang order. Bilang resulta, ang aming mga elemento, na pinagsunod-sunod ayon sa id, ay nawawalan ng pagkakasunud-sunod. Hindi maganda. Kailangan nating baguhin nang 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 nagsimulang magmukhang medyo nakakatakot, ngunit ngayon ang problema ay nalutas nang tama. Magbasa nang higit pa tungkol sa iba't ibang pagpapangkat dito: Maaari kang lumikha ng iyong sariling kolektor. Magbasa nang higit pa dito: "Paggawa ng custom na kolektor sa Java 8" . At makikinabang ka sa pagbabasa ng talakayan dito: "Java 8 list to map with stream" .

Fall-trap

Comparatorat Comparablemagaling. Ngunit mayroong isang nuance na dapat mong tandaan. Kapag ang isang klase ay nagsasagawa ng pag-uuri, inaasahan nito na ang iyong klase ay maaaring ma-convert sa isang Comparable. Kung hindi ito ang kaso, makakatanggap ka ng error sa oras ng pagtakbo. 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 mabibigo sa isang error: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable At lahat dahil sinubukan nitong pag-uri-uriin ang mga elemento (ito ay isang SortedSet, pagkatapos ng lahat)...ngunit hindi magawa. Huwag kalimutan ito kapag nagtatrabaho sa SortedMapat SortedSet.

Higit pang pagbabasa:

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