JavaRush /Java блогы /Random-KK /Java тіліндегі компаратор
Viacheslav
Деңгей

Java тіліндегі компаратор

Топта жарияланған
Тек жалқау адамдар Java тілінде Comparator және салыстыру туралы жазбаған. Мен жалқау емеспін, сондықтан мен сізден тағы бір нұсқаны жақсы көруіңізді және ұнатуыңызды сұраймын. Бұл артық болмайды деп үміттенемін. Иә, бұл мақала: «Сіз жадтан компаратор жаза аласыз ба?» Деген сұраққа жауап. Осы мақаланы оқығаннан кейін әркім жадынан компаратор жаза алады деп үміттенемін.
Java тіліндегі компаратор - 1
Кіріспе Java тілі an objectіге бағытталған тіл екені белгілі. Нәтижесінде Java тілінде an objectілермен жұмыс істеу әдеттегідей. Бірақ ерте ме, кеш пе, қандай да бір принцип бойынша an objectілерді салыстыру міндеті туындайды. Сонымен, берілген: Бізде Message класы сипаттайтын кейбір хабарлама бар:
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;
    }
}
Осы сыныпты Tutorialspoint java компиляторына қосамыз . Сондай-ақ импортты қосуды ұмытпайық:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
Негізгі әдісте біз бірнеше хабарлама жасаймыз:
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);
}
Оларды салыстырғымыз келсе, не істеуіміз керек екенін ойланайық? Мысалы, біз идентификатор бойынша сұрыптағымыз келеді. Ал реттілік жасау үшін қандай нысан алдыңғы (яғни кішірек) және қайсысы келесі (яғни үлкенірек) екенін түсіну үшін an objectілерді қандай да бір жолмен салыстыру керек. Java.lang.Object сияқты сыныптан бастайық . Біз білетіндей, барлық класстар осы Object сыныбынан жанама түрде мұра алады. Және бұл қисынды, өйткені Бұл «барлығы an object» деген ұғымды білдіреді және барлық сыныптар үшін жалпы мінез-құлықты қамтамасыз етеді. Және бұл класс әр сыныптың екі әдісі бар екенін анықтайды: → hashCode hashCode әдісі сынып данасы ретінде нысанның кейбір сандық (int) көрінісін қайтарады. Бұл нені білдіреді? Бұл дегеніміз, егер сіз сыныптың екі түрлі данасын жасасаңыз, онда даналар әртүрлі болғандықтан, олардың хэшcodeы әртүрлі болуы керек. Бұл әдіс сипаттамасында былай делінген: «Қандай практикалық болса да, Object сыныбымен анықталған hashCode әдісі әртүрлі нысандар үшін әртүрлі бүтін сандарды қайтарады» Яғни, егер бұл екі түрлі даналар болса, онда олар әртүрлі болуы керек. хэш codeтары. Яғни, бұл әдіс біздің салыстыруымызға жарамайды. → тең Теңдеу әдісі «an objectілер тең» деген сұраққа жауап береді және логикалық мәнді қайтарады. Бұл әдістің әдепкі codeы бар:
public boolean equals(Object obj) {
    return (this == obj);
}
Яғни, нысанда бұл әдісті қайта белгілемей, бұл әдіс нысанға сілтемелердің сәйкес келетінін немесе сәйкес келмейтінін көрсетеді. Бұл біздің хабарламаларымыз үшін жарамсыз, өйткені бізді нысанға сілтемелер қызықтырмайды, бізді хабар идентификаторы қызықтырады. Тіпті егер біз теңдеу әдісін жоққа шығарсақ, біз алатын максималды мән: «Олар тең» немесе «Олар тең емес». Бірақ бұл бізге тәртіпті анықтау үшін жеткіліксіз.

Java тілінде салыстырмалы және салыстырмалы

Бізге не сәйкес келеді? Аудармашыда «салыстыру» сөзін ағылшын тіліне аударсақ, біз «салыстыр» аудармасын аламыз. Керемет, онда бізге салыстыратын адам керек. Егер сіз осы салыстыруды салыстырсаңыз, онда салыстырушы - Салыстырушы. Java Api ашайық және сол жерден Comparator табайық . Және шынымен де, мұндай интерфейс бар - java.util.Comparator java.util.Comparator және java.lang.Comparable Көріп отырғаныңыздай, мұндай интерфейс бар. Оны жүзеге асыратын сынып «Мен an objectілерді салыстыру функциясын жүзеге асырып жатырмын» дейді. Есте сақтау керек жалғыз нәрсе - салыстырмалы келісім-шарт, ол келесідей көрінеді:

Comparator возвращает int по следующей схеме: 
  • отрицательный int (первый an object отрицательный, то есть меньше)
  • положительный int (первый an object положительный, хороший, то есть больший)
  • ноль = an objectы равны
Енді салыстырмалы түрде жазайық. Бізге java.util.Comparator импорттау қажет болады . Импорттаудан кейін main әдісіне әдісті қосыңыз: Comparator<Message> comparator = new Comparator<Message>(); Әрине, бұл жұмыс істемейді, өйткені Comparator - бұл интерфейс. Сондықтан жақшадан кейін бұйра жақшаларды қосамыз { }. Бұл жақшаға біз әдісті жазамыз:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Мұны жазуды есте сақтаудың қажеті жоқ. Салыстырушы – салыстыруды жүзеге асыратын, яғни салыстыру жасайтын адам. Салыстырылған an objectілер қандай ретпен орналасады деген сұраққа жауап беру үшін int қайтарамыз. Мұның бәрі, шын мәнінде. Қарапайым және оңай. Мысалдан көріп отырғанымыздай, Comparator-тен басқа, тағы бір интерфейс бар - java.lang.Comparable , оны жүзеге асыру үшін біз compareTo әдісін анықтауымыз керек . Бұл интерфейс «Интерфейсті жүзеге асыратын сынып сынып даналарын салыстыруға мүмкіндік береді» дейді. Мысалы, Integer-тің салыстыруTo іске асыруы келесідей көрінеді:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Барлық осы интерфейстерді қалай есте сақтауға болады? Не үшін? Барлығы ағылшын тілінен келеді. Салыстыру – салыстыру, салыстыру – Салыстырғыш (мысалы, тіркеуші ретінде. Яғни, тіркеуші), ал «салыстырмалы» сын есімі Comparable. Ал, «Салыстыр» сөзі тек салыстырумен ғана емес, салыстырумен де аударылады. Бәрі оңай. Java тілін ағылшын тілінде сөйлейтін адамдар жазды және Java тілінде барлығын атағанда олар жай ғана ағылшын тілін басшылыққа алды және атауда қандай да бір логика болды. Ал compareTo әдісі сынып данасын басқа даналармен қалай салыстыру керектігін сипаттайды. Мысалы, жолдар лексиграфиялық түрде салыстырылады , ал сандар мән бойынша салыстырылады.
Java тіліндегі компаратор - 2
Java 8 жақсы өзгерістер әкелді. Егер біз Comparator интерфейсіне мұқият қарасақ, оның үстінде annotation бар екенін көреміз @FunctionalInterface. Шын мәнінде, бұл annotation ақпаратқа арналған және бұл интерфейстің жұмыс істейтінін білдіреді. Бұл интерфейсте іске асырусыз тек 1 дерексіз әдіс бар екенін білдіреді. Бұл бізге не береді? Салыстырғыш codeын енді былай жаза аламыз:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Жақшада айнымалыларды қалай атайтынымыз көрсетілген. Java өзі мұны көреді, өйткені ... Егер бір ғана әдіс болса, онда қандай енгізу параметрлері, қанша және қандай түрлер қажет екені анық. Әрі қарай, біз оларды codeтың осы бөліміне көшіргіміз келетінін көрсеткі арқылы айтамыз. Сонымен қатар, Java 8 арқасында интерфейстерде әдепкі әдістер пайда болды - бұл интерфейсті жүзеге асырған кезде әдепкі бойынша (әдепкі бойынша) пайда болатын әдістер. Comparator интерфейсінде олардың бірнешеуі бар. Мысалы:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Кодыңызды тазартатын басқа әдіс бар. Жоғарыда келтірілген мысалды қарастырайық, мұнда біз компараторымызды сипаттадық. Ол не істеп жатыр? Бұл өте қарапайым. Ол жай ғана нысанды алып, одан салыстырмалы мәнді шығарады. Мысалы, Integer салыстырмалы түрде жүзеге асырады, сондықтан хабар идентификаторы мәндерінде салыстыру орындай алдық. Бұл қарапайым салыстырмалы функцияны былай жазуға болады:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Яғни, тура мағынада: «Бізде мынандай салыстыратын Comparator бар: ол нысандарды алады, олардан getId() әдісін пайдаланып Comparable алады, compareTo арқылы салыстырады». Енді қорқынышты дизайн жоқ. Соңында тағы бір ерекшелікті атап өткім келеді. Салыстырғыштарды біріктіруге болады. Мысалы:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Қолдану

Салыстырмалы мәлімдеме өте қисынды болып шықты, солай емес пе? Енді оны қалай, қандай жерлерде қолдану керектігін көруіміз керек. → Collections.sort (java.util.Collections) Әрине, коллекцияларды осылай сұрыптауға болады. Бірақ бәрі емес, тек тізімдер. Бұл жерде ерекше ештеңе жоқ, өйткені... Бұл индекс бойынша элементке кіруді талап ететін тізім. Бұл екінші элементті үшінші элементпен ауыстыруға мүмкіндік береді. Сондықтан, осылай сұрыптау тізімдер үшін ғана мүмкін:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort (java.util.Arrays) Массивтерді сұрыптау да ыңғайлы. Тағы да, индекс бойынша элементтерге қол жеткізудің сол себепті. → java.util.SortedSet және java.util.SortedMap ұрпақтары Естеріңізге сала кетейік, Set және Map жазбаларды сақтау тәртібіне кепілдік бермейді. БІРАҚ бізде тәртіпке кепілдік беретін арнайы енгізулер бар. Ал егер коллекция элементтері java.lang.Comparable қолданбаса, онда біз Comparator параметрін осындай жинақтардың конструкторына бере аламыз:
Set<Message> msgSet = new TreeSet(comparator);
Stream API Java 8-де пайда болған Stream Api бағдарламасында компаратор ағын элементтерімен жұмысты жеңілдетуге мүмкіндік береді. Мысалы, бізге 0-ден 999-ға дейінгі кездейсоқ сандар тізбегі қажет:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Біз тоқтай аламыз, бірақ одан да қызықты мәселелер бар. Мысалы, сізге Картаны дайындау керек, мұнда кілт хабарлама идентификаторы болып табылады. Сонымен бірге, біз бұл кілттерді ең кішкентайдан ең үлкенге қарай реттелетіндей етіп сұрыптағымыз келеді. Осы codeтан бастайық:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Біз мұнда қайтып алатынымыз - бұл HashMap. Және біз білетіндей, ол ешқандай тапсырысқа кепілдік бермейді. Сондықтан ID бойынша сұрыпталған жазбаларымыз істен шықты. Дұрыс емес. Біз коллекторды сәл өзгертуіміз керек:
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));
Код сәл қорқынышты болып көрінді, бірақ TreeMap нақты іске асырылуының арқасында мәселе дұрыс шешілді. Әртүрлі топтар туралы толығырақ мына жерден оқи аласыз: Коллекторды өзіңіз жасай аласыз. Толығырақ мына жерден оқи аласыз: «Java 8 жүйесінде пайдаланушы коллекторын жасау» . Бұл жерде талқылауды оқу пайдалы: "Java 8 тізімі ағынмен картаға түсіру" .
Java тіліндегі компаратор - 3
Comparator және Comparable тырмалар жақсы. Бірақ олармен байланысты бір нюанс бар, ол есте сақтауға тұрарлық. Сынып сұрыптауды орындаған кезде, ол сіздің сыныпты Салыстырмалыға шығара алатынын есептейді. Егер бұлай болмаса, сіз орындау уақытында қате аласыз. Мысал қарастырайық:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Бұл жерде ешнәрсе жоқ сияқты. Бірақ шын мәнінде, біздің мысалда ол қателікпен бұзылады: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Барлығы элементтерді сұрыптауға тырысқандықтан (бұл SortedSet, түптеп келгенде). Ал мен алмадым. SortedMap және SortedSet бағдарламаларымен жұмыс істегенде мұны есте сақтау керек. Көруге қосымша ұсынылады: Юрий Ткач: HashSet және TreeSet - №1 жинақтар - кеңейтілген Java
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION