जावामध्ये तुलना करणारे आणि तुलना करण्याबद्दल लिहिणारे फक्त आळशी नाहीत. मी आळशी नाही, म्हणून कृपया आणखी एका स्पष्टीकरणाबद्दल प्रेम करा आणि पकडा. मला आशा आहे की ते अनावश्यक होणार नाही. आणि हो, हा लेख या प्रश्नाचे उत्तर आहे: " तुम्ही मेमरीमधून तुलनाकर्ता लिहू शकता का? " मला आशा आहे की हा लेख वाचल्यानंतर प्रत्येकजण मेमरीमधून तुलना करणारा लिहू शकेल. Javas Comparator इंटरफेस - 1

परिचय

तुम्हाला माहिती आहेच, जावा ही ऑब्जेक्ट ओरिएंटेड भाषा आहे. परिणामी, Java मधील वस्तू हाताळण्याची प्रथा आहे. परंतु लवकरच किंवा नंतर, आपल्याला काही वैशिष्ट्यांवर आधारित वस्तूंची तुलना करण्याचे कार्य सामोरे जावे लागेल. उदाहरणार्थ : समजा आपल्याकडे वर्गाने वर्णन केलेला काही संदेश आहे 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;
पद्धतीमध्ये main, अनेक संदेश तयार करा:

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);
}
विचार करूया की त्यांची तुलना करायची असेल तर काय करायचे? उदाहरणार्थ, आम्हाला आयडीनुसार क्रमवारी लावायची आहे. आणि ऑर्डर तयार करण्यासाठी, कोणती वस्तू प्रथम आली पाहिजे (म्हणजे लहान) आणि कोणते अनुसरण करावे (म्हणजे मोठे) हे समजून घेण्यासाठी आपल्याला कोणत्या तरी वस्तूंची तुलना करणे आवश्यक आहे. चला java.lang.Object सारख्या वर्गापासून सुरुवात करूया . आम्हाला माहित आहे की सर्व वर्ग निहितपणे Objectवर्गाचा वारसा घेतात. आणि हे अर्थपूर्ण आहे कारण ते संकल्पना प्रतिबिंबित करते की "सर्व काही एक वस्तू आहे" आणि सर्व वर्गांसाठी समान वर्तन प्रदान करते. हा वर्ग ठरवतो की प्रत्येक वर्गात दोन पद्धती आहेत: → hashCode पद्धत hashCodeकाही संख्यात्मक मिळवते (int) ऑब्जेक्टचे प्रतिनिधित्व. याचा अर्थ काय? याचा अर्थ असा की जर तुम्ही वर्गाची दोन वेगवेगळी उदाहरणे तयार केली तर त्यांना वेगवेगळे hashCodes असावेत. पद्धतीचे वर्णन तितकेच सांगते: "जेवढी वाजवी व्यावहारिक आहे, क्लास ऑब्जेक्टद्वारे परिभाषित केलेली हॅशकोड पद्धत वेगळ्या वस्तूंसाठी भिन्न पूर्णांक मिळवते". दुसऱ्या शब्दांत, दोन भिन्न instances साठी, भिन्न s असावा hashCode. म्हणजेच, ही पद्धत आमच्या तुलनेत योग्य नाही. → equals_ पद्धत equals"या वस्तू समान आहेत का?" या प्रश्नाचे उत्तर देते. आणि एक boolean." डीफॉल्टनुसार, या पद्धतीमध्ये खालील कोड आहे:

public boolean equals(Object obj) {
    return (this == obj);
}
म्हणजेच, जर ही पद्धत ओव्हरराइड केली नसेल, तर ते मूलत: ऑब्जेक्ट संदर्भ जुळत आहे की नाही हे सांगते. आम्हाला आमच्या संदेशांसाठी हे हवे आहे, कारण आम्हाला संदेश आयडीमध्ये स्वारस्य आहे, ऑब्जेक्ट संदर्भांमध्ये नाही. आणि जरी आपण equalsपद्धत ओव्हरराइड केली तरीही, आपण सर्वात जास्त आशा करू शकतो की ते समान आहेत की नाही हे शिकणे. आणि ऑर्डर निश्चित करण्यासाठी आमच्यासाठी हे पुरेसे नाही. मग आम्हाला काय हवे आहे? आम्हाला तुलना करणारे काहीतरी हवे आहे. जो तुलना करतो तो एक आहे Comparator. Java API उघडा आणि Comparator शोधा . java.util.Comparatorखरंच, एक इंटरफेस आहेjava.util.Comparator and java.util.Comparable जसे आपण पाहू शकता, असा इंटरफेस अस्तित्वात आहे. त्याची अंमलबजावणी करणारा वर्ग म्हणतो, "मी वस्तूंची तुलना करणारी पद्धत अंमलात आणतो." तुम्हाला फक्त एकच गोष्ट लक्षात ठेवण्याची गरज आहे ती म्हणजे तुलनाकर्ता करार, जो खालीलप्रमाणे व्यक्त केला जातो:

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
आता एक तुलनाकर्ता लिहू. आम्हाला आयात करावे लागेल java.util.Comparator. इंपोर्ट स्टेटमेंट नंतर, पद्धतीमध्ये खालील जोडा main: Comparator<Message> comparator = new Comparator<Message>(); अर्थात, हे कार्य करणार नाही, कारण Comparatorएक इंटरफेस आहे. {}म्हणून आम्ही कंसानंतर कुरळे ब्रेसेस जोडतो . ब्रेसेसमध्ये खालील पद्धत लिहा:

public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
तुम्हाला स्पेलिंग लक्षात ठेवण्याचीही गरज नाही. तुलनाकर्ता तो असतो जो तुलना करतो, म्हणजेच तो तुलना करतो. ऑब्जेक्ट्सचा सापेक्ष क्रम दर्शवण्यासाठी, आम्ही एक परत करतो int. मुळात तेच आहे. छान आणि सोपे. तुम्ही उदाहरणावरून पाहू शकता की, तुलनाकर्ता व्यतिरिक्त, आणखी एक इंटरफेस आहे — java.lang.Comparable, ज्यासाठी आम्हाला पद्धत लागू करणे आवश्यक आहे compareTo. हा इंटरफेस म्हणतो, "मला लागू करणारा वर्ग वर्गाच्या उदाहरणांची तुलना करणे शक्य करतो." उदाहरणार्थ, To Integerची अंमलबजावणी compareखालीलप्रमाणे आहे:

(x < y) ? -1 : ((x == y) ? 0 : 1)
Java 8 ने काही छान बदल सादर केले. तुम्ही इंटरफेस जवळून पाहिल्यास , तुम्हाला त्यावर भाष्य Comparatorदिसेल . @FunctionalInterfaceहे भाष्य माहितीच्या उद्देशाने आहे आणि आम्हाला सांगते की हा इंटरफेस कार्यशील आहे. याचा अर्थ या इंटरफेसमध्ये फक्त 1 अमूर्त पद्धत आहे, जी अंमलबजावणी नसलेली पद्धत आहे. हे आपल्याला काय देते? आता आपण तुलनाकर्त्याचा कोड याप्रमाणे लिहू शकतो:

Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
आम्ही कंसात व्हेरिएबल्सची नावे देतो. Java पाहेल की एकच पद्धत असल्यामुळे, आवश्यक संख्या आणि इनपुट पॅरामीटर्सचे प्रकार स्पष्ट आहेत. मग आम्ही त्यांना कोडच्या या भागात पास करण्यासाठी बाण ऑपरेटर वापरतो. आणखी काय, Java 8 ला धन्यवाद, आमच्याकडे आता इंटरफेसमध्ये डीफॉल्ट पद्धती आहेत. जेव्हा आम्ही इंटरफेस लागू करतो तेव्हा या पद्धती डीफॉल्टनुसार दिसतात. इंटरफेसमध्ये Comparatorअनेक आहेत. उदाहरणार्थ:

Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
आणखी एक पद्धत आहे जी तुमचा कोड क्लीनर करेल. वरील उदाहरणावर एक नजर टाका, जिथे आम्ही आमचा तुलनाकर्ता परिभाषित केला आहे. ते काय करते? ते अगदी आदिम आहे. हे फक्त एक ऑब्जेक्ट घेते आणि काही मूल्य काढते जे "तुलनायोग्य" आहे. उदाहरणार्थ, Integerऔजारे comparable, त्यामुळे आम्ही मेसेज आयडी फील्डच्या मूल्यांवर तुलना करण्यासाठी ऑपरेशन करण्यास सक्षम आहोत. हे साधे तुलनात्मक कार्य असे लिहिले जाऊ शकते:

Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
दुसऱ्या शब्दांत, आमच्याकडे Comparatorअशी तुलना आहे: ते वस्तू घेते, त्यांच्याकडून getId()मिळवण्यासाठी पद्धत वापरते आणि नंतर तुलना करण्यासाठी वापरते. आणि आणखी भयानक बांधकामे नाहीत. आणि शेवटी, मला आणखी एक वैशिष्ट्य लक्षात घ्यायचे आहे. तुलना करणार्‍यांना साखळी बांधली जाऊ शकते. उदाहरणार्थ: ComparablecompareTo

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) अॅरे क्रमवारी लावणे सोपे आहे. पुन्हा, त्याच कारणास्तव — त्यांचे घटक अनुक्रमणिकेद्वारे ऍक्सेस केले जातात. → Descendants of java.util.SortedSet and java.util.SortedMap तुम्हाला ते आठवेल Setआणि Mapघटक कोणत्या क्रमाने साठवले जातात याची हमी देत ​​नाही. परंतु, आमच्याकडे विशेष अंमलबजावणी आहेत जी ऑर्डरची हमी देतात. आणि जर कलेक्शनचे घटक अंमलात आणत नसतील , तर आम्ही त्याच्या कन्स्ट्रक्टरला java.util.Comparableपास करू शकतो :Comparator

Set<Message> msgSet = new TreeSet(comparator);
Stream API स्ट्रीम API मध्ये, जे Java 8 मध्ये दिसले, तुलनाकर्ते तुम्हाला प्रवाह घटकांसह कार्य सुलभ करू देतात. उदाहरणार्थ, समजा आम्हाला 0 ते 999 पर्यंत यादृच्छिक संख्यांचा क्रम हवा आहे, ज्यामध्ये समावेश आहे:

Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
आम्ही येथे थांबू शकतो, परंतु आणखी मनोरंजक समस्या आहेत. उदाहरणार्थ, समजा तुम्हाला एक तयार करणे आवश्यक आहे Map, जिथे की एक संदेश आयडी आहे. याव्यतिरिक्त, आम्हाला या की क्रमवारी लावायच्या आहेत, म्हणून आम्ही खालील कोडसह प्रारंभ करू:

Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
आम्ही प्रत्यक्षात येथे मिळवा HashMap. आणि आम्हाला माहित आहे की, ते कोणत्याही ऑर्डरची हमी देत ​​​​नाही. परिणामी, आमचे घटक, जे आयडीनुसार क्रमवारी लावलेले होते, त्यांचा क्रम गमावला. चांगले नाही. आम्हाला आमचा कलेक्टर थोडा बदलावा लागेल:

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));
कोड थोडा भयानक दिसू लागला आहे, परंतु आता समस्या योग्यरित्या सोडवली गेली आहे. येथे विविध गटांबद्दल अधिक वाचा: तुम्ही तुमचा स्वतःचा कलेक्टर तयार करू शकता. येथे अधिक वाचा: "जावा 8 मध्ये सानुकूल कलेक्टर तयार करणे" . आणि तुम्हाला येथे चर्चा वाचून फायदा होईल: "प्रवाहासह नकाशा करण्यासाठी Java 8 सूची" .

फॉल-ट्रॅप

Comparatorआणि Comparableचांगले आहेत. परंतु एक बारकावे आहे जे आपण लक्षात ठेवले पाहिजे. जेव्हा एखादा वर्ग वर्गीकरण करतो, तेव्हा तो अपेक्षा करतो की तुमचा वर्ग एक मध्ये रूपांतरित केला जाऊ शकतो 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.