Java Language
तुलनीय और तुलनात्मक
खोज…
वाक्य - विन्यास
- सार्वजनिक वर्ग MyClass की तुलना तुलनीय है
<MyClass
> - सार्वजनिक वर्ग MyComparator तुलनाकर्ता लागू करता है
<SomeOtherClass
> - सार्वजनिक int तुलना (MyClass अन्य)
- सार्वजनिक int तुलना (SomeOtherClass o1, SomeOtherClass o2)
टिप्पणियों
एक compareTo(..)
विधि को लागू करते समय जो एक double
पर निर्भर करता है, निम्नलिखित कार्य न करें:
public int comareTo(MyClass other) {
return (int)(doubleField - other.doubleField); //THIS IS BAD
}
ट्र (int)
कास्ट के कारण होने वाला ट्रंकेशन एक सकारात्मक या नकारात्मक संख्या के बजाय कभी-कभी गलत तरीके से 0
वापस करने का कारण होगा, और इस प्रकार बग की तुलना और सॉर्टिंग हो सकती है।
इसके बजाय, सबसे सरल सही कार्यान्वयन Double.compare का उपयोग करना है , जैसे:
public int comareTo(MyClass other) {
return Double.compare(doubleField,other.doubleField); //THIS IS GOOD
}
Comparable<T>
का एक गैर-जेनेरिक संस्करण, केवल Comparable
, जावा 1.2 के बाद से अस्तित्व में है । विरासत कोड के साथ इंटरफेस करने के अलावा, जेनेरिक संस्करण Comparable<T>
को लागू करना हमेशा बेहतर होता है, क्योंकि इसकी तुलना में कास्टिंग की आवश्यकता नहीं होती है।
यह एक वर्ग के लिए अपने आप से तुलना करने के लिए बहुत मानक है, जैसे:
public class A implements Comparable<A>
हालांकि इस प्रतिमान से टूटना संभव है, ऐसा करते समय सतर्क रहें।
यदि एक वर्ग के उदाहरणों पर एक Comparator<T>
उपयोग किया जा सकता है यदि वह वर्ग Comparable<T>
लागू करता है। इस मामले में, Comparator
के तर्क का उपयोग किया जाएगा; Comparable
कार्यान्वयन द्वारा निर्दिष्ट प्राकृतिक आदेश की अनदेखी की जाएगी।
तुलनीय का उपयोग करके एक सूची को क्रमबद्ध करना या एक तुलनित्र
कहते हैं कि हम एक वर्ग पर काम कर रहे हैं जो अपने पहले और अंतिम नामों से एक व्यक्ति का प्रतिनिधित्व करते हैं। हमने ऐसा करने के लिए एक बुनियादी वर्ग बनाया है और उचित equals
और hashCode
विधियों को लागू किया है।
public class Person {
private final String lastName; //invariant - nonnull
private final String firstName; //invariant - nonnull
public Person(String firstName, String lastName){
this.firstName = firstName != null ? firstName : "";
this.lastName = lastName != null ? lastName : "";
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return lastName + ", " + firstName;
}
@Override
public boolean equals(Object o) {
if (! (o instanceof Person)) return false;
Person p = (Person)o;
return firstName.equals(p.firstName) && lastName.equals(p.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
}
अब हम की एक सूची क्रमबद्ध करना चाहते हैं Person
इस तरह के निम्न परिदृश्य की तरह उनके नाम से वस्तुओं,:
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //This currently won't work.
}
दुर्भाग्य से, के रूप में चिह्नित, ऊपर वर्तमान में संकलन नहीं होगा। Collections.sort(..)
केवल एक सूची को सॉर्ट करना जानता है यदि उस सूची के तत्व तुलनीय हैं, या तुलना का एक कस्टम तरीका दिया गया है।
यदि आपको निम्नलिखित सूची को क्रमबद्ध करने के लिए कहा गया: 1,3,5,4,2
, तो आपको यह कहने में कोई दिक्कत नहीं होगी कि उत्तर 1,2,3,4,5
। ऐसा इसलिए है क्योंकि इंटेगर (जावा और गणितीय दोनों में) एक प्राकृतिक ऑर्डरिंग , एक मानक, डिफ़ॉल्ट तुलना आधार ऑर्डरिंग है। हमारे व्यक्ति वर्ग को एक स्वाभाविक क्रम देने के लिए, हम तुलना योग्य Comparable<Person>
को लागू करते हैं, जिसके लिए विधि compareTo(Person p):
को लागू करने की आवश्यकता होती है compareTo(Person p):
public class Person implements Comparable<Person> {
private final String lastName; //invariant - nonnull
private final String firstName; //invariant - nonnull
public Person(String firstName, String lastName) {
this.firstName = firstName != null ? firstName : "";
this.lastName = lastName != null ? lastName : "";
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String toString() {
return lastName + ", " + firstName;
}
@Override
public boolean equals(Object o) {
if (! (o instanceof Person)) return false;
Person p = (Person)o;
return firstName.equals(p.firstName) && lastName.equals(p.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
@Override
public int compareTo(Person other) {
// If this' lastName and other's lastName are not comparably equivalent,
// Compare this to other by comparing their last names.
// Otherwise, compare this to other by comparing their first names
int lastNameCompare = lastName.compareTo(other.lastName);
if (lastNameCompare != 0) {
return lastNameCompare;
} else {
return firstName.compareTo(other.firstName);
}
}
}
अब, दी गई मुख्य विधि सही ढंग से कार्य करेगी
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Now functions correctly
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}
यदि, हालांकि, आप या तो नहीं चाहते हैं या वर्ग Person
को संशोधित करने में असमर्थ हैं, तो आप एक कस्टम Comparator<T>
प्रदान कर सकते हैं Comparator<T>
जो कि किसी भी दो Person
वस्तुओं की तुलना को संभालता है। यदि आपको निम्नलिखित सूची को क्रमबद्ध करने के लिए कहा गया है: circle, square, rectangle, triangle, hexagon
आप नहीं कर सकते, लेकिन यदि आपको कोनों की संख्या के आधार पर उस सूची को क्रमबद्ध करने के लिए कहा गया, तो आप कर सकते हैं। बस, इसलिए, एक तुलनित्र प्रदान करता है जावा को निर्देश देता है कि दो सामान्य रूप से तुलनीय वस्तुओं की तुलना कैसे करें।
public class PersonComparator implements Comparator<Person> {
public int compare(Person p1, Person p2) {
// If p1's lastName and p2's lastName are not comparably equivalent,
// Compare p1 to p2 by comparing their last names.
// Otherwise, compare p1 to p2 by comparing their first names
if (p1.getLastName().compareTo(p2.getLastName()) != 0) {
return p1.getLastName().compareTo(p2.getLastName());
} else {
return p1.getFirstName().compareTo(p2.getFirstName());
}
}
}
//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Illegal, Person doesn't implement Comparable.
Collections.sort(people, new PersonComparator()); //Legal
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
}
एक अनाम आंतरिक वर्ग के रूप में तुलनाकर्ताओं का निर्माण / उपयोग किया जा सकता है
//Assume the first version of Person (that does not implement Comparable) is used here
public static void main(String[] args) {
List<Person> people = Arrays.asList(new Person("John", "Doe"),
new Person("Bob", "Dole"),
new Person("Ronald", "McDonald"),
new Person("Alice", "McDonald"),
new Person("Jill", "Doe"));
Collections.sort(people); //Illegal, Person doesn't implement Comparable.
Collections.sort(people, new PersonComparator()); //Legal
//people is now sorted by last name, then first name:
// --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald
//Anonymous Class
Collections.sort(people, new Comparator<Person>() { //Legal
public int compare(Person p1, Person p2) {
//Method code...
}
});
}
लैम्ब्डा अभिव्यक्ति आधारित तुलनाकर्ता
जावा 8 के रूप में, तुलनित्र को लंबोदर भाव के रूप में भी व्यक्त किया जा सकता है
//Lambda
Collections.sort(people, (p1, p2) -> { //Legal
//Method code....
});
तुलनित्र डिफ़ॉल्ट तरीके
इसके अलावा, तुलनाकारक के निर्माण के लिए तुलनाकारी इंटरफेस पर दिलचस्प डिफ़ॉल्ट तरीके हैं: निम्नलिखित एक तुलनित्र द्वारा की तुलना बनाता lastName
और फिर firstName
।
Collections.sort(people, Comparator.comparing(Person::getLastName)
.thenComparing(Person::getFirstName));
एक तुलनित्र के आदेश को उलटाते हुए
किसी भी तुलनित्र को उलटमरथोड का उपयोग करके आसानी से उलटा किया जा सकता reversedMethod
जो आरोही क्रम में अवरोही में बदल जाएगा।
तुलना और तरीकों की तुलना करें
Comparable<T>
इंटरफ़ेस के लिए एक विधि की आवश्यकता है:
public interface Comparable<T> {
public int compareTo(T other);
}
और Comparator<T>
इंटरफ़ेस को एक विधि की आवश्यकता है:
public interface Comparator<T> {
public int compare(T t1, T t2);
}
इन दोनों तरीकों अनिवार्य रूप से एक नाबालिग अंतर के साथ एक ही बात करते हैं,: compareTo
तुलना this
के लिए other
, जबकि compare
तुलना t1
को t2
, के बारे में बिल्कुल भी ध्यान रखने वाली नहीं this
।
उस अंतर के अलावा, दोनों विधियों की समान आवश्यकताएं हैं। विशेष रूप से ( तुलना के लिए), ऑर्डर के लिए निर्दिष्ट ऑब्जेक्ट के साथ इस ऑब्जेक्ट की तुलना करता है। एक नकारात्मक पूर्णांक, शून्य या एक सकारात्मक पूर्णांक लौटाता है क्योंकि यह ऑब्जेक्ट निर्दिष्ट ऑब्जेक्ट से कम, बराबर या उससे अधिक है। इस प्रकार, a
और b
की तुलना के लिए:
- यदि
a < b
,a.compareTo(b)
औरcompare(a,b)
को एक नकारात्मक पूर्णांक वापस करना चाहिए, औरb.compareTo(a)
औरcompare(b,a)
एक धनात्मक पूर्णांक को वापस करना चाहिए। - यदि
a > b
,a.compareTo(b)
औरcompare(a,b)
को एक धनात्मक पूर्णांक वापस करना चाहिए, औरb.compareTo(a)
औरcompare(b,a)
एक ऋणात्मक पूर्णांक वापस करना चाहिए - यदि तुलना के लिए
a
बराबरb
है, तो सभी तुलनाओं को0
वापस करना चाहिए।
प्राकृतिक (तुलनीय) बनाम स्पष्ट (तुलना) छँटाई
दो Collections.sort()
विधियाँ:
- एक जो
List<T>
को एक पैरामीटर के रूप में लेता है जहांT
को तुलनीय लागू करना चाहिए औरcompareTo()
पद्धति को ओवरराइड करना चाहिए जो क्रमबद्धता को निर्धारित करता है। - एक जो एक सूची और एक तुर्क को तर्क के रूप में लेता है, जहां तुलनित्र क्रम को निर्धारित करता है।
सबसे पहले, यहाँ एक व्यक्ति वर्ग है जो तुलना करता है:
public class Person implements Comparable<Person> {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.getAge() - o.getAge();
}
@Override
public String toString() {
return this.getAge()+"-"+this.getName();
}
}
यहाँ बताया गया है कि आप उपर्युक्त वर्ग का उपयोग उसके तत्वों के प्राकृतिक क्रम में एक सूची को क्रमबद्ध करने के लिए कैसे करेंगे, यह compareTo()
ओवरराइड द्वारा परिभाषित किया गया है:
//-- usage
List<Person> pList = new ArrayList<Person>();
Person p = new Person();
p.setName("A");
p.setAge(10);
pList.add(p);
p = new Person();
p.setName("Z");
p.setAge(20);
pList.add(p);
p = new Person();
p.setName("D");
p.setAge(30);
pList.add(p);
//-- natural sorting i.e comes with object implementation, by age
Collections.sort(pList);
System.out.println(pList);
यहां बताया गया है कि आप किसी सूची को क्रमबद्ध करने के लिए एक अनाम इनलाइन कम्प्रेसर का उपयोग कैसे करेंगे, जो तुलनात्मक लागू नहीं करता है, या इस मामले में, प्राकृतिक ऑर्डर करने के अलावा अन्य किसी क्रम में सूची को सॉर्ट करने के लिए:
//-- explicit sorting, define sort on another property here goes with name
Collections.sort(pList, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(pList);
मानचित्र प्रविष्टियों को क्रमबद्ध करना
जावा 8 के अनुसार, मानचित्र पुनरावृत्तियों को छाँटने की अनुमति देने के लिए Map.Entry
इंटरफ़ेस पर डिफ़ॉल्ट विधियाँ हैं।
Map<String, Integer> numberOfEmployees = new HashMap<>();
numberOfEmployees.put("executives", 10);
numberOfEmployees.put("human ressources", 32);
numberOfEmployees.put("accounting", 12);
numberOfEmployees.put("IT", 100);
// Output the smallest departement in terms of number of employees
numberOfEmployees.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.limit(1)
.forEach(System.out::println); // outputs : executives=10
बेशक, ये धारा एपी के बाहर भी इस्तेमाल किए जा सकते हैं:
List<Map.Entry<String, Integer>> entries = new ArrayList<>(numberOfEmployees.entrySet());
Collections.sort(entries, Map.Entry.comparingByValue());
तुलना विधि का उपयोग कर एक तुलनित्र बनाना
Comparator.comparing(Person::getName)
यह वर्ग Person
लिए एक तुलनित्र बनाता है जो इस व्यक्ति का नाम तुलना स्रोत के रूप में उपयोग करता है। साथ ही लंबे, इंट और डबल की तुलना करने के लिए विधि संस्करण का उपयोग करना संभव है। उदाहरण के लिए:
Comparator.comparingInt(Person::getAge)
उलटा क्रम
एक तुलनित्र बनाने के लिए जो रिवर्स ऑर्डरिंग उपयोग को reversed()
विधि:
Comparator.comparing(Person::getName).reversed()
तुलकों की श्रृंखला
Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName)
यह एक तुलनित्र बनायेगा जो पहले नाम के साथ तुलना करता है, फिर पहले नाम से तुलना करता है। आप जितने चाहें उतने तुलनाकर्ताओं को चेन कर सकते हैं।