आलसी केवल जावा में तुलनित्र और तुलना के बारे में लिखने वाले नहीं हैं। मैं आलसी नहीं हूँ, इसलिए कृपया एक और स्पष्टीकरण के बारे में प्यार और शिकायत करें। मुझे आशा है कि यह अतिश्योक्तिपूर्ण नहीं होगा। और हाँ, यह लेख प्रश्न का उत्तर है: " क्या आप स्मृति से एक तुलनित्र लिख सकते हैं? " मुझे आशा है कि इस लेख को पढ़ने के बाद हर कोई स्मृति से तुलनित्र लिखने में सक्षम होगा।

परिचय
जैसा कि आप जानते हैं, जावा एक वस्तु-उन्मुख भाषा है। नतीजतन, यह जावा में वस्तुओं में हेरफेर करने के लिए प्रथागत है। लेकिन जल्दी या बाद में, आप कुछ विशेषताओं के आधार पर वस्तुओं की तुलना करने के कार्य का सामना करते हैं। उदाहरण के लिए : मान लीजिए कि हमारे पास कक्षा द्वारा वर्णित कुछ संदेश हैं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
) वस्तु का प्रतिनिधित्व। इसका क्या मतलब है? इसका मतलब है कि यदि आप एक वर्ग के दो अलग-अलग उदाहरण बनाते हैं, तो उनके अलग-अलग hashCode
एस होने चाहिए। विधि का विवरण उतना ही कहता है: "जितना उचित व्यावहारिक है, क्लास ऑब्जेक्ट द्वारा परिभाषित हैशकोड विधि अलग-अलग वस्तुओं के लिए अलग-अलग पूर्णांक लौटाती है"। दूसरे शब्दों में, दो अलग-अलग एस के लिए अलग-अलग एस instance
होना चाहिए । hashCode
अर्थात् यह विधि हमारी तुलना के लिए उपयुक्त नहीं है। → equals
. विधि equals
प्रश्न का उत्तर देती है "क्या ये वस्तुएं समान हैं?" और रिटर्न a boolean
।" डिफ़ॉल्ट रूप से, इस विधि में निम्न कोड है:
public boolean equals(Object obj) {
return (this == obj);
}
यही है, अगर यह विधि ओवरराइड नहीं है, तो यह अनिवार्य रूप से कहता है कि ऑब्जेक्ट संदर्भ मेल खाता है या नहीं। यह वह नहीं है जो हम अपने संदेशों के लिए चाहते हैं, क्योंकि हम संदेश आईडी में रुचि रखते हैं, वस्तु संदर्भों में नहीं। और यहां तक कि अगर हम equals
विधि को ओवरराइड करते हैं, तो हम सबसे ज्यादा उम्मीद कर सकते हैं कि वे समान हैं या नहीं। और यह हमारे लिए क्रम निर्धारित करने के लिए पर्याप्त नहीं है। तो फिर हमें क्या चाहिए? हमें कुछ ऐसा चाहिए जो तुलना करे। तुलना करने वाला एक है 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)
जावा 8 ने कुछ अच्छे बदलाव पेश किए। यदि आप इंटरफ़ेस पर करीब से नज़र डालते हैं , तो आप इसके ऊपर एनोटेशन Comparator
देखेंगे । @FunctionalInterface
यह एनोटेशन सूचना उद्देश्यों के लिए है और हमें बताता है कि यह इंटरफ़ेस कार्यशील है। इसका मतलब है कि इस इंटरफ़ेस में केवल 1 अमूर्त विधि है, जो कार्यान्वयन के बिना एक विधि है। यह हमें क्या देता है? अब हम तुलनित्र का कोड इस तरह लिख सकते हैं:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
हम चर को कोष्ठक में नाम देते हैं। जावा यह देखेगा कि क्योंकि केवल एक ही विधि है, तो आवश्यक संख्या और प्रकार के इनपुट पैरामीटर स्पष्ट हैं। फिर हम उन्हें कोड के इस हिस्से में पास करने के लिए एरो ऑपरेटर का उपयोग करते हैं। इसके अलावा, जावा 8 के लिए धन्यवाद, अब हमारे पास इंटरफेस में डिफ़ॉल्ट तरीके हैं। जब हम एक इंटरफ़ेस लागू करते हैं तो ये विधियाँ डिफ़ॉल्ट रूप से दिखाई देती हैं। इंटरफ़ेस Comparator
में कई हैं। उदाहरण के लिए:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
एक और तरीका है जो आपके कोड को क्लीनर बना देगा। ऊपर दिए गए उदाहरण पर एक नज़र डालें, जहाँ हमने अपने तुलनित्र को परिभाषित किया है। इससे क्या होता है? यह काफी आदिम है। यह बस एक वस्तु लेता है और कुछ मूल्य निकालता है जो "तुलनीय" होता है। उदाहरण के लिए, Integer
लागू करता है comparable
, इसलिए हम संदेश आईडी फ़ील्ड के मानों पर तुलना करने के लिए ऑपरेशन करने में सक्षम हैं। यह सरल तुलनित्र कार्य इस प्रकार लिखा जा सकता है:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
दूसरे शब्दों में, हमारे पास a है Comparator
जो इस तरह तुलना करता है: यह ऑब्जेक्ट लेता है, उनसे getId()
a प्राप्त करने के लिए विधि का उपयोग करता है, और फिर तुलना करने के लिए उपयोग करता है। और अधिक भयानक निर्माण नहीं हैं। और अंत में, मैं एक और विशेषता नोट करना चाहता हूं। तुलनित्रों को जंजीर किया जा सकता है। उदाहरण के लिए: 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)
सारणियों को क्रमबद्ध करना भी आसान है। दोबारा, उसी कारण से - उनके तत्वों को इंडेक्स द्वारा एक्सेस किया जाता है। → Descendants of java.util.SortedSet and java.util.SortedMap
आप इसे याद करेंगे Set
और Map
उस क्रम की गारंटी नहीं देंगे जिसमें तत्व संग्रहीत हैं। लेकिन, हमारे पास विशेष कार्यान्वयन हैं जो आदेश की गारंटी देते हैं। और यदि संग्रह के तत्व लागू नहीं होते हैं java.util.Comparable
, तो हम Comparator
इसके निर्माता को a पास कर सकते हैं:
Set<Message> msgSet = new TreeSet(comparator);
→ Stream API
स्ट्रीम एपीआई में, जो जावा 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 में एक कस्टम कलेक्टर बनाना" । और आपको यहां चर्चा पढ़ने से लाभ होगा: "जावा 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
।
अधिक पढ़ना: |
---|
GO TO FULL VERSION