சோம்பேறிகள் ஜாவாவில் ஒப்பீடுகள் மற்றும் ஒப்பீடுகளைப் பற்றி எழுதுவது மட்டுமல்ல. நான் சோம்பேறி இல்லை, எனவே தயவு செய்து மற்றொரு விளக்கத்தை விரும்புங்கள். அது மிகையாகாது என்று நம்புகிறேன். ஆம், இந்த கட்டுரை கேள்விக்கான பதில்: " நினைவகத்திலிருந்து ஒரு ஒப்பீட்டாளரை எழுத முடியுமா? " இந்த கட்டுரையைப் படித்த பிறகு ஒவ்வொருவரும் நினைவகத்திலிருந்து ஒரு ஒப்பீட்டாளரை எழுத முடியும் என்று நம்புகிறேன்.
அறிமுகம்
உங்களுக்கு தெரியும், ஜாவா ஒரு பொருள் சார்ந்த மொழி. இதன் விளைவாக, ஜாவாவில் பொருட்களைக் கையாளுவது வழக்கம். ஆனால் விரைவில் அல்லது பின்னர், சில பண்புகளின் அடிப்படையில் பொருட்களை ஒப்பிடும் பணியை நீங்கள் எதிர்கொள்கிறீர்கள். எடுத்துக்காட்டாக : வகுப்பினால் விவரிக்கப்பட்ட சில செய்திகள் எங்களிடம் இருப்பதாக வைத்துக்கொள்வோம்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;
}
}
இந்த வகுப்பை டுடோரியல்ஸ்பாயிண்ட் ஜாவா கம்பைலரில் வைக்கவும் . இறக்குமதி அறிக்கைகளையும் சேர்க்க மறக்காதீர்கள்:
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
s க்கு, வெவ்வேறு s இருக்க வேண்டும் hashCode
. அதாவது, இந்த முறை எங்கள் ஒப்பீட்டிற்கு ஏற்றது அல்ல. → equals
_ இந்த equals
முறை "இந்த பொருள்கள் சமமானதா?" என்ற கேள்விக்கு பதிலளிக்கிறது. மற்றும் ஒரு boolean
.
public boolean equals(Object obj) {
return (this == obj);
}
அதாவது, இந்த முறை மேலெழுதப்படாவிட்டால், பொருள் குறிப்புகள் பொருந்துமா இல்லையா என்பதை இது முக்கியமாகக் கூறுகிறது. எங்கள் செய்திகளுக்கு இது தேவையில்லை, ஏனெனில் நாங்கள் செய்தி ஐடிகளில் ஆர்வமாக உள்ளோம், பொருள் குறிப்புகளில் அல்ல. இந்த முறையை நாம் புறக்கணித்தாலும் equals
, அவை சமமானவையா என்பதை அறிந்து கொள்வதே நாம் அதிகம் எதிர்பார்க்க முடியும். வரிசையைத் தீர்மானிக்க இது போதாது. அப்படியானால் நமக்கு என்ன தேவை? ஒப்பிடும் ஒன்று நமக்குத் தேவை. ஒப்பிடுபவர் ஒரு Comparator
. ஜாவா API ஐத் திறந்து ஒப்பீட்டாளரைக் கண்டறியவும் . 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());
வேறு வார்த்தைகளில் கூறுவதானால், எங்களிடம் 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)
வரிசைகளை வரிசைப்படுத்துவதும் எளிதானது. மீண்டும், அதே காரணத்திற்காக - அவற்றின் கூறுகள் குறியீட்டால் அணுகப்படுகின்றன. → Descendants of java.util.SortedSet and java.util.SortedMap
நீங்கள் அதை நினைவுபடுத்துவீர்கள் Set
மற்றும் Map
உறுப்புகள் சேமிக்கப்படும் வரிசைக்கு உத்தரவாதம் அளிக்க மாட்டீர்கள். ஆனால், ஆர்டருக்கு உத்தரவாதம் அளிக்கும் சிறப்பு செயலாக்கங்கள் எங்களிடம் உள்ளன. சேகரிப்பின் கூறுகள் செயல்படுத்தப்படாவிட்டால் , அதன் கட்டமைப்பாளருக்கு java.util.Comparable
நாம் அனுப்பலாம் :Comparator
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));
குறியீடு கொஞ்சம் பயங்கரமாகத் தோன்றத் தொடங்கியது, ஆனால் இப்போது சிக்கல் சரியாக தீர்க்கப்பட்டது. பல்வேறு குழுக்களைப் பற்றி இங்கே மேலும் படிக்கவும்:
நீங்கள் உங்கள் சொந்த சேகரிப்பாளரை உருவாக்கலாம். மேலும் இங்கே படிக்கவும்: "Java 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