JavaRush /Java Blogu /Random-AZ /Java-da müqayisə
Viacheslav
Səviyyə

Java-da müqayisə

Qrupda dərc edilmişdir
Yalnız tənbəl insanlar Java-da Comparator və müqayisə haqqında yazmamışdır. Mən tənbəl deyiləm - buna görə də sizdən daha bir dəyişikliyi sevməyinizi və üstünlük vermənizi xahiş edirəm. Ümid edirəm ki, artıq olmaz. Bəli, bu məqalə sualın cavabıdır: "Yaddaşdan müqayisə yaza bilərsinizmi?" Ümid edirəm ki, bu yazını oxuyandan sonra hər kəs yaddaşdan müqayisə yaza biləcək.
Java-da müqayisəçi - 1
Giriş Java obyekt yönümlü dil kimi tanınır. Nəticədə Java-da obyektlərlə işləmək adi haldır. Ancaq gec-tez obyektləri hansısa prinsipə görə müqayisə etmək vəzifəsi yaranır. Beləliklə, verilmişdir: Mesaj sinfi tərəfindən təsvir olunan bəzi mesajımız var:
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;
    }
}
Gəlin bu sinfi Tutorialspoint java kompilyatoruna əlavə edək . Gəlin idxalı da əlavə etməyi unutmayın:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
Əsas metodda bir neçə mesaj yaradacağıq:
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);
}
Gəlin fikirləşək, əgər onları müqayisə etmək istəyiriksə, nə etməliyik? Məsələn, biz id-yə görə çeşidləmək istəyirik. Və nizam yaratmaq üçün hansı obyektin əvvəlki (yəni daha kiçik) və hansının növbəti (yəni daha böyük) olduğunu başa düşmək üçün obyektləri birtəhər müqayisə etməlisiniz. Java.lang.Object kimi bir siniflə başlayaq . Bildiyimiz kimi, bütün siniflər bu Obyekt sinfindən dolayı miras alır. Və bu məntiqlidir, çünki Bu, mahiyyətcə, konsepsiyanı ifadə edir: "Hər şey bir obyektdir" və bütün siniflər üçün ümumi davranışı təmin edir. Və bu sinif müəyyən edir ki, hər bir sinfin iki metodu var: → hashCode hashCode metodu obyektin bəzi rəqəmli (int) təsvirini sinfin nümunəsi kimi qaytarır. Bunun mənası nədi? Bu o deməkdir ki, əgər siz bir sinfin iki fərqli nümunəsini yaratsanız, nümunələr fərqli olduğundan, onların hashCode fərqli olmalıdır. Metodun təsvirində belə deyilir: “Obyekt sinfi tərəfindən təyin olunan hashCode metodu nə qədər praktikdirsə, fərqli obyektlər üçün fərqli tam ədədlər qaytarır” Yəni, əgər bunlar iki fərqli nümunədirsə, onda onlar fərqli olmalıdır. hashCodes. Yəni bu üsul bizim müqayisəmiz üçün uyğun deyil. → bərabərdir Bərabər metodu “obyektlər bərabərdir” sualına cavab verir və məntiqi qaytarır. Bu metodun standart kodu var:
public boolean equals(Object obj) {
    return (this == obj);
}
Yəni, obyektdə bu metodu ləğv etmədən, bu metod mahiyyətcə obyektə istinadların uyğun olub-olmadığını bildirir. Bu, mesajlarımız üçün uyğun deyil, çünki biz obyektə keçidlərlə maraqlanmırıq, mesaj id-si ilə maraqlanırıq. Bərabər metodu ləğv etsək belə, əldə edəcəyimiz maksimum: “Onlar bərabərdir” və ya “Onlar bərabər deyillər”. Amma bu, sifarişi müəyyən etmək üçün bizə kifayət etmir.

Java-da Comparator və Comparator

Bizə nə yaraşır? Tərcüməçidə “müqayisə et” sözünü ingilis dilinə tərcümə etsək, “müqayisə et” tərcüməsini alacağıq. Əla, onda bizə müqayisə edəcək biri lazımdır. Əgər bu müqayisəni müqayisə etsəniz, müqayisə edən Müqayisə edəndir. Gəlin Java Api-ni açaq və orada Comparator tapaq . Və həqiqətən də belə bir interfeys var - java.util.Comparator java.util.Comparator və java.lang.Comparable Gördüyünüz kimi belə bir interfeys var. Onu həyata keçirən sinif deyir ki, “Mən obyektləri müqayisə etmək funksiyasını həyata keçirirəm”. Həqiqətən yadda saxlamaq lazım olan yeganə şey aşağıdakı kimi ifadə olunan müqayisə müqaviləsidir:

Comparator возвращает int по следующей схеме: 
  • отрицательный int (первый an object отрицательный, то есть меньше)
  • положительный int (первый an object положительный, хороший, то есть больший)
  • ноль = an objectы равны
İndi bir müqayisə yazaq. Biz java.util.Comparator idxal etməliyik . İdxaldan sonra main-a metod əlavə edin: Comparator<Message> comparator = new Comparator<Message>(); Təbii ki, bu işləməyəcək, çünki Comparator interfeysdir. Buna görə də, mötərizələrdən sonra buruqları əlavə edəcəyik { }. Bu mötərizədə metodu yazacağıq:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Bunu yazmağı xatırlamağa belə ehtiyac yoxdur. Müqayisə edən, yəni müqayisə aparan şəxsdir. Müqayisə olunan obyektlərin hansı sırada olması sualına cavab vermək üçün int-i qaytarırıq. Əslində hamısı budur. Sadə və asanlıqla. Nümunədən göründüyü kimi, Comparator-dan əlavə, başqa bir interfeys də var - java.lang.Comparable , onu həyata keçirərkən, biz compareTo metodunu müəyyən etməliyik . Bu interfeys deyir ki, "İnterfeys həyata keçirən sinif, sinfin nümunələrini müqayisə etməyə imkan verir." Məsələn, Integer-in compareTo tətbiqi belə görünür:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Bütün bu interfeysləri necə yadda saxlamaq olar? Nə üçün? Hər şey ingilis dilindən gəlir. Müqayisə - müqayisə etmək, müqayisə edən Müqayisəçidir (məsələn, qeydiyyatçı kimi. Yəni qeydiyyatdan keçən), “müqayisə edilmiş” sifəti isə Müqayisə olunur. Yaxşı, “Müqayisə et” təkcə müqayisə ilə deyil, həm də müqayisə kimi tərcümə olunur. Bu sadədir. Java dilini ingilisdilli insanlar yazırdılar və Java-da hər şeyi adlandırarkən sadəcə ingilis dilini rəhbər tuturdular və adlandırmada bir növ məntiq var idi. CompareTo metodu isə sinif nümunəsinin digər instansiyalarla necə müqayisə edilməli olduğunu təsvir edir. Məsələn, sətirlər leksiqrafik , rəqəmlər isə dəyərlə müqayisə edilir.
Java-da müqayisə aparatı - 2
Java 8 bəzi gözəl dəyişikliklər gətirdi. Comparator interfeysinə diqqətlə baxsaq, onun üstündə annotasiya olduğunu görərik @FunctionalInterface. Əslində, bu annotasiya məlumat üçündür və bu interfeysin funksional olduğunu bildirir. Bu o deməkdir ki, bu interfeysin tətbiqi olmadan yalnız 1 mücərrəd metodu var. Bu bizə nə verir? İndi müqayisə kodunu belə yaza bilərik:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Mötərizədə dəyişənləri necə adlandırdığımız göstərilir. Java özü bunu görəcək, çünki... Yalnız bir üsul varsa, o zaman hansı giriş parametrlərinin, neçə və hansı növlərin lazım olduğu aydındır. Sonra, biz onları kodun bu bölməsinə köçürmək istədiyimizi ox ilə deyirik. Bundan əlavə, Java 8 sayəsində interfeyslərdə defolt üsullar meydana çıxdı - bunlar interfeys tətbiq edərkən standart olaraq (standart olaraq) görünən üsullardır. Comparator interfeysində bunlardan bir neçəsi var. Məsələn:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Kodunuzu daha təmiz edəcək başqa bir üsul var. Yuxarıdakı nümunəyə baxaq, burada müqayisə aparatımız təsvir edilmişdir. O nə edir? Olduqca primitivdir. O, sadəcə olaraq obyekti götürür və ondan müqayisə edilə bilən bəzi dəyər çıxarır. Məsələn, Integer müqayisə edilə bilən tətbiqləri həyata keçirir, buna görə də mesaj id dəyərlərində compareTo yerinə yetirə bildik. Bu sadə müqayisə funksiyası da belə yazıla bilər:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Yəni, sözün əsl mənasında, “Bizim belə müqayisə edən bir Müqayisəçimiz var: o, obyektləri götürür, getId() metodundan istifadə edərək onlardan Müqayisə olunur, müqayisəTo istifadə edərək müqayisə edir.” Və daha dəhşətli dizaynlar yoxdur. Və nəhayət, daha bir xüsusiyyəti qeyd etmək istərdim. Komparatorlar bir-birinə zəncirlə bağlana bilər. Misal üçün:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Ərizə

Müqayisəli bəyannamə olduqca məntiqli çıxdı, elə deyilmi? İndi onu necə və hansı yerlərdə istifadə edəcəyimizi görməliyik. → Collections.sort (java.util.Collections) Əlbəttə, biz kolleksiyaları bu şəkildə sıralaya bilərik. Ancaq hər şey deyil, sadəcə siyahılar. Və burada qeyri-adi heç nə yoxdur, çünki... Bu, indeks üzrə elementə giriş tələb edən siyahıdır. Və bu, iki nömrəli elementi üçüncü elementlə dəyişdirməyə imkan verir. Buna görə də, bu şəkildə çeşidləmə yalnız siyahılar üçün mümkündür:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort (java.util.Arrays) Massivləri çeşidləmək də əlverişlidir. Yenə eyni səbəbdən elementlərə indekslə daxil olmaq. → java.util.SortedSet və java.util.SortedMap nəsilləri xatırladığımız kimi, Set və Map qeydlərin saxlanma qaydasına zəmanət vermir. AMMA qaydaya zəmanət verən xüsusi tətbiqlərimiz var. Əgər kolleksiya elementləri java.lang.Comparable tətbiq etmirsə, onda biz Comparator-u belə kolleksiyaların konstruktoruna ötürə bilərik:
Set<Message> msgSet = new TreeSet(comparator);
Stream API Java 8-də ortaya çıxan Stream Api-də müqayisəedici axın elementləri üzərində işi sadələşdirməyə imkan verir. Məsələn, 0-dan 999-a qədər təsadüfi ədədlər ardıcıllığına ehtiyacımız var:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Biz dayana bilərdik, amma daha maraqlı problemlər var. Məsələn, açarın mesaj identifikatoru olduğu Xəritə hazırlamalısınız. Eyni zamanda, biz bu açarları elə çeşidləmək istəyirik ki, düymələr kiçikdən böyüyə doğru qaydasında olsun. Bu kodla başlayaq:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Burada geri alacağımız şey əslində HashMapdır. Və bildiyimiz kimi, heç bir sifarişə zəmanət vermir. Ona görə də şəxsiyyət vəsiqəsinə görə çeşidlənən qeydlərimiz sadəcə sıradan çıxıb. Yaxşı deyil. Kollektorumuzu bir az dəyişməli olacağıq:
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));
Kod bir az daha qorxunc görünürdü, lakin TreeMap-ın açıq şəkildə tətbiqi sayəsində problem indi düzgün həll edildi. Müxtəlif qruplar haqqında daha ətraflı burada oxuya bilərsiniz: Kollektoru özünüz yarada bilərsiniz. Daha ətraflı burada oxuya bilərsiniz: "Java 8-də xüsusi kollektorun yaradılması" . Və burada müzakirəni oxumaq faydalıdır: "Axınla xəritə üçün Java 8 siyahısı" .
Java-da müqayisə aparatı - 3
Comparator və Comparable tırmıklar yaxşıdır. Ancaq onlarla əlaqəli bir nüans var ki, xatırlamağa dəyər. Bir sinif çeşidləmə həyata keçirdikdə, o, sinifinizi Müqayisə edilə bilənə çevirə biləcəyini hesablayır. Əgər belə deyilsə, icra zamanı xəta alacaqsınız. Bir misala baxaq:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Deyəsən burada heç nə yoxdur. Amma əslində, bizim nümunəmizdə o, xəta ilə qəzaya uğrayacaq: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Və hamısı elementləri çeşidləməyə çalışdığı üçün (Bu, SortedSet-dir). Mən bacarmadım. SortedMap və SortedSet ilə işləyərkən bunu yadda saxlamalısınız. Baxmaq üçün əlavə olaraq tövsiyə olunur: Yuri Tkach: HashSet və TreeSet - Kolleksiyalar #1 - Təkmil Java
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION