User Tools

Site Tools


java:jfc_intf_sort

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Next revision Both sides next revision
java:jfc_intf_sort [2017/03/28 23:25]
gthanos [Υλοποιώντας το interface Comparable για δικούς μας τύπους δεδομένων]
java:jfc_intf_sort [2020/03/15 18:42]
gthanos [Υλοποιώντας την ισότητα αντικειμένων τύπου Student]
Line 1: Line 1:
-====== Ορίζοντας την σειρά καταχώρησης και ανάκτησης των δεδομένων ======+====== Ανίχνευση ισότητας και σύγκριση αντικειμένων ======
  
-Κατά κανόνα όταν θέλουμε να συγκρίνουμε επιμέρους αντικείμενα προκειμένου να ορίσουμε την σειρά αποθήκευσης και ανάκτησης τους σε μία δομή δεδομένων αυτό θα πρέπει να ακολουθεί κάποιους κανόνες. Για παράδειγμα, εάν συγκρίνουμε δύο String, αυτά θα συγκριθούν με βάση συγκεκριμένα κριτήρια λεξικού. Αντίστοιχα, αν συγκρίνουμε δύο ημερομηνίες αυτές θα συγκριθούν με βάση τις ημερομηνίες που αντιπροσωπεύουν. Γενικότερα στην java ορίζεται το interface //[[http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html|Comparable]]// το οποίο ορίζει τα κριτήρια σύγκρισης μεταξύ δύο αντικειμένων του ιδίου τύπου. Οι κλάσεις της standard βιβλιοθήκης String, Date, Integer κ.α. υλοποιούν το συγκεκριμένο interface ορίζοντας τους κανόνες σύγκρισης. Παρακάτω δίνεται μία σειρά από δημοφιλείς κλάσεις της java και τα κριτήρια υλοποίησης του συγκεκριμένου interface για κάθε μία από αυτές.+===== Ανίχνευση ισότητας αντικειμένων =====
  
-{{ :java:object-ordering.png?450 |}}+Εάν θέλουμε να αποθηκεύσουμε σε ένα //hash table// μία σειρά αντικειμένων θα πρέπει να είμαστε σίγουροι ότι δεν θα προστεθούν σε αυτό δύο ίδια αντικείμενα. Κατά συνέπεια, πρέπει να είμαστε σε θέση να προσδιορίσουμε με συνέπεια την ισότητα δύο αντικειμένων ιδίου τύπου
  
-===== Υλοποιώντας το interface Comparable για δικούς μας τύπους δεδομένων =====+Προκειμένου να μπορούμε να ανιχνεύουμε την ισότητα μεταξύ αντικειμένων η Java παρέχει τις μεθόδους [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-|public boolean equals(Object obj)]] και [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--|public int hashCode()]] της κλάσης [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html|java.lang.Object]]. Εάν θέλετε να μπορείτε να ανιχνεύσετε ισότητα/ανισότητα για τα αντικείμενα μίας κλάσης θα πρέπει να είναι υλοποιημένες __και οι δύο μέθοδοι__. Η μέθοδος //equals// επιστρέφει //true// εάν δύο αντικείμενα είναι ίσα, διαφορετικά επιστρέφει //false//. Η μέθοδος //hashCode// πρέπει να επιστρέφει την ίδια ακέραια τιμή για δύο διαφορετικά αλλά ίσα αντικείμενα. 
  
-Παρακάτω δίνεται η υλοποίηση του interface //Comparable// για την κλάση Name. +==== Υλοποιώντας την ισότητα αντικειμένων τύπου Student ====
-<code java Name.java> +
-import java.util.*;+
  
-public class Name implements Comparable<Name>+Παρακάτω δίνεται η κλάση Student ενημερωμένη ώστε να περιέχει τις υλοποιήσεις των συναρτήσεων [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-|equals]] και [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--|hashCode]].
-  private final String firstName, lastName;+
  
-  public Name(String firstNameString lastName) { +<code java Student.java> 
-    if (firstName == null || lastName == null) +public class Student { 
-      throw new NullPointerException(); +  private String firstName
-    this.firstName = firstName+  private String lastName; 
-    this.lastName = lastName;+   
 +  public Student(String fname, String lname{ 
 +    firstName = fname
 +    lastName = lname;
   }   }
- +   
-  public String firstName() { return firstName; } +  public void setFirstName( String firstName ) { 
-  public String lastName()  { return lastName; +    this.firstName firstName;  
- +
-  public boolean equals(Object o) { +
-    if (!(o instanceof Name)) +
-      return false; +
-    Name n = (Name) o; +
-    return n.firstName.equals(firstName) && n.lastName.equals(lastName);+
   }   }
- +   
-  public int hashCode() { +  public void setLastNameString lastName ) { 
-    return 31*firstName.hashCode() + lastName.hashCode();+    this.lastName = lastName 
   }   }
 +   
 +  public String getFirstName() { 
 +    return firstName; 
 +  } 
 +   
 +  public String getLastName() { 
 +    return lastName; 
 +  } 
 +       
   public String toString() {   public String toString() {
-  return firstName + " " + lastName;+    return firstName+" "+lastName;
   }   }
- +   
-  public int compareTo(Name n) { +  public boolean equals(Object o) { 
-    int lastCmp = lastName.compareTo(n.lastName); +    return (o instanceof Student && o.hashCode() == this.hashCode()); 
-    return (lastCmp != 0 ? lastCmp : firstName.compareTo(n.firstName));+  } 
 +   
 +  public int hashCode() { 
 +    return 3 * firstName.hashCode() + 5 * lastName.hashCode();
   }   }
 } }
 </code> </code>
  
-Τα βασικά χαρακτηριστικά της παραπάνω κλάσης είναι τα εξής: +Παρατηρήστε ότι η μέθοδος equals επιστρέφει true εάν η παράμετρος ''Object ο'' είναι του τύπου της τρέχουσας κλάσης (δηλαδή Studentκαι έχει την ίδια τιμή hashCode με το τρέχον αντικείμενο. Αυτός είναι ένας τυπικός τρόπος για να ορίσετε τη συγκεκριμένη συνάρτηση.
-  * Έλεγχος στον κατασκευαστή εάν περνάνε ή όχι null ορίσματα. Αυτό μας προστατεύει από το να έρθουμε αντιμέτωποι με //NullPointerException// στην συνέχεια. +
-  * Η μέθοδος //equal()// επαναπροσδιορίζεται, έτσι ώστε να συγκρίνονται τα πεδία της κλάσης Name μεταξύ τους. +
-  * Η μέθοδος //hashCode()// επαναπροσδιορίζεται. Ο λόγος που πρέπει να επαναπροσδιορίσουμε την μέθοδο αυτή είναι ότι εξ' ορισμού δύο ίδια αντικείμενα θα πρέπει να έχουν και ίδια //hashCodes//. Εφόσον άλλαξε η μέθοδος //equal()// θα αλλάξει και η μέθοδος //hashCode()// ώστε να επιστρέφει το ίδιο hash για αντικείμενα που η equals επιστρέφει ισότητα. +
-  * Υλοποιείται η μέθοδος //int comparTo(T t)//.+
  
-<WRAP 85% center round tip> +Η μέθοδος hashCode πρέπει να επιστρέφει την ίδια ακέραια τιμή για δύο ίσα αντικείμενα. Για το συγκεκριμένο παράδειγμα είναι αναγκαίο να έχουμε υπόψη μας ότι η κλάση [[https://docs.oracle.com/javase/8/docs/api/java/lang/String.html|java.lang.String]] υλοποιεί τη συνάρτηση [[https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#hashCode--|hashCode]] υπακούοντας στη συγκεκριμένη αρχή. Η υλοποίηση στη συνάρτηση Student αποκλείει να έχουμε δύο διαφορετικά αντικείμενα όπου το ένα είναι διαφορετικό από το άλλο αλλά έχουν το ίδιo hashCode().
-Εξ' ορισμού (εάν δεν την επαναπροσδιορίσετε) η μέθοδος [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-|equals]][[http://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html|συγκρίνει δύο αντικείμενα με βάση την διεύθυνση του κάθε αντικειμένου]]. Στην πραγματικότητα επιστρέφει ισότητα __μόνο αν τα references των δύο αντικειμένων είναι ίδια__, δηλ. και οι δύο μεταβλητές δείχνουν στο ίδιο αντικείμενο. Στην πλειοψηφία των περιπτώσεων ο συγκεκριμένος τρόπος σύκρισης δεν μας δίνει στην πράξη ικανοποιητικό αποτελέσμα, καθώς αγνοεί αντικείμενα με τα ίδια περιεχόμενα.+
  
-Σε αναλογία η μέθοδος [[https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--|hashCode]] επιστρέφει την δεκαεξαδική τιμή που δείχνει η μεταβλητή του αντικειμένου στην μνήμη. Με δεδομένο ότι __για δύο ίδια αντικείμενα τα hashCodes τους θα πρέπει να είναι κοινά__, η default υλοποίηση ταιριάζει με την default υλοποίηση της μεθόδου //equals//. Αν όμως αλλάξει η υλοποίηση της //equals// σε μία κλάση θα πρέπει να αλλάξει και η //hashCode()// ώστε δύο ίδια αντικείμενα κατά την μέθοδο //equals// να έχουν και ίδια hashCodes. +===== Σύγκριση αντικειμένων =====
-</WRAP>+
  
-<code java NameSort.java> +Εάν θέλουμε να αποθηκεύσουμε αντικείμενα σε μία δεντρική δομή λεξικού (π.χ. [[https://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html|java.util.TreeSet]] είναι αναγκαίο να ορίσουμε τον τρόπο σύγκρισης των αντικειμένων μεταξύ τους. Η ίδια ανάγκη προκύπτει έαν θέλουμε να ταξινομήσουμε ένα σύνολο στοιχείων ή να εφαρμόσουμε δυαδική αναζήτηση σε ένα ήδη ταξινομημένο σύνολο.
-import java.util.*;+
  
-public class NameSort { +Η διαδικασία της σύγκρισης επιτυγχάνεται υλοποιώντας το //interface// [[http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html|java.lang.Comparable]] (λεπτομέρειες [[java:comparable|εδώ]]), το οποίο ορίζει το κριτήριο σύγκρισης μεταξύ δύο αντικειμένων ιδίου τύπου. Οι κλάσεις της βασικής βιβλιοθήκης [[https://docs.oracle.com/javase/8/docs/api/java/lang/String.html|java.lang.String]], [[https://docs.oracle.com/javase/8/docs/api/java/util/Date.html|java.util.Date]] κ.α. υλοποιούν το συγκεκριμένο interface ορίζοντας τον κανόνα σύγκρισης μεταξύ δύο αντικειμένων. Παρακάτω δίνεται μία σειρά από δημοφιλείς κλάσεις της java και τα κριτήρια υλοποίησης του συγκεκριμένου interface για κάθε μία από αυτές.
-  public static void main(String[] args+
-    Name nameArray[] = { +
-      new Name("John""Smith"), +
-      new Name("Karl", "Ng"), +
-      new Name("Jeff", "Smith"), +
-      new Name("Tom", "Rich"+
-    };+
  
-    List<Namenames Arrays.asList(nameArray); +{{ :java:object-ordering.png?450 |}} 
-    Collections.sort(names); + 
-    System.out.println(names);+==== Υλοποιώντας το interface Comparable για δικούς μας τύπους δεδομένων ==== 
 + 
 +Παρακάτω δίνεται η υλοποίηση του interface [[https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html|java.lang.Comparable]] για την κλάση Student. Η υλοποίηση της μεθόδου compareTo συγκρίνει τα επίθετα των δύο φοιτητών. Εάν είναι διαφορετικά επιστρέφει τη λεξικογραφική διαφορά τους. Εάν  είναι ίδια επιστρέφει τη λεξικογραφική διαφορά των ονομάτων τους. 
 + 
 +<code java Student.java> 
 +public class Student implements java.lang.Comparable<Student>
 +  private String firstName; 
 +  private String lastName; 
 +   
 +  public Student(String fname, String lname) { 
 +    firstName fname; 
 +    lastName = lname; 
 +  } 
 +   
 +  public void setFirstName( String firstName ) { 
 +    this.firstName = firstName;   
 +  } 
 +   
 +  public void setLastNameString lastName 
 +    this.lastName = lastName  
 +  } 
 +   
 +  public String getFirstName() { 
 +    return firstName; 
 +  } 
 +   
 +  public String getLastName() { 
 +    return lastName; 
 +  } 
 +        
 +  public String toString() { 
 +    return firstName+" "+lastName; 
 +  } 
 +   
 +  public boolean equals(Object o) { 
 +    return (o instanceof Student && o.hashCode() == this.hashCode()); 
 +  } 
 +   
 +  public int hashCode() { 
 +    return 3 * firstName.hashCode() + 5 * lastName.hashCode(); 
 +  } 
 +   
 +  public int compareTo(Student st) { 
 +    return lastName.compareTo(st.lastName) != 0 ? lastName.compareTo(st.lastName) : firstName.compareTo(st.firstName);
   }   }
 } }
 </code> </code>
  
-===== Συγκρίνοντας με χρήσης ενός Comparator object =====+===== Συγκρίνοντας με χρήση ενός Comparator object =====
  
-Ας υποθέσουμε ότι θέλουμε να συγκρίνουμε αντικείμενα με διαφορετική μέθοδο από την μέθοδο με την οποία συγκρίνονται ή ότι θέλουμε να συγκρίνουμε αντικείμενα τα οποία δεν υλοποιούν το interface //Comparable//. Σε αυτή την περίπτωση θα χρειαστείουμε ένα βοηθητικό αντικείμενο του τύπου [[http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html|Comparator]], όπως παρακάτω+[[:java:comparable#%CF%84%CE%BF_interface_javautilcomparator|Επαναλαμβάνοντας τη συλλογιστική]] για το interface [[https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html|java.util.Comparator]], ας υποθέσουμε ότι θέλουμε να συγκρίνουμε τα αντικείμενα τύπου Student με διαφορετική πολιτική από αυτή που είναι υλοποιημένη στη μέθοδο ''compareTo'' ή ότι θέλουμε να συγκρίνουμε αντικείμενα τα οποία δεν υλοποιούν το interface //Comparable//. Σε αυτή την περίπτωση θα χρειαστούμε μία βοηθητική κλάση του τύπου [[http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html|java.util.Comparator]], όπως παρακάτω
  
-<code java NameComparatorSort.java>+<code java StudentComparator.java>
 import java.util.*; import java.util.*;
-public class NameComparatorSort { +public class StudentComparator implements java.util.Comparator<Student> { 
-    static final Comparator<NameNameOrdering =  +  public int compare(Student s1Student s2) { 
-                                        new Comparator<Name>() +    return s1.getLastName().compareTo(s2.getLastName()); 
-            public int compare(Name n1Name n2) { +  }
-                int cmp = n1.firstName().compareTo(n2.firstName() ); +
-                return (cmp != 0 ? cmp : n1.lastName().compareTo(n2.lastName()); +
-            } +
-    }; +
- +
-    public static void main(String[] args) { +
-        Name nameArray[] = { +
-            new Name("John", "Smith"), +
-            new Name("Karl", "Ng"), +
-            new Name("Jeff", "Smith"), +
-            new Name("Tom", "Rich"+
-        }; +
-        Collections.sort(Arrays.asList(nameArray), NameOrdering); +
-        for(Name n: nameArray) +
-        System.out.println(n); +
-    }+
 } }
 </code> </code>
- 
-Ο συγκεκριμένος //Comparator// μας δίνει την δυνατότητα να αλλάξουμε την μέθοδο με την οποία συγκρίνονται τα αντικείμενα του συγκεκριμένου τύπου δεδομένων δίνοντας προτεραιότητα στο μικρό όνομα αντί για το επίθετο. 
  
 |Προηγούμενο: [[:java:jfc_intf_map | Interface java.util.Map ]] | [[:toc | Περιεχόμενα ]] | Επόμενο: [[:java:jfc_intf_sorted_set_map | SortedSet και SortedMap ]] | |Προηγούμενο: [[:java:jfc_intf_map | Interface java.util.Map ]] | [[:toc | Περιεχόμενα ]] | Επόμενο: [[:java:jfc_intf_sorted_set_map | SortedSet και SortedMap ]] |
  
java/jfc_intf_sort.txt · Last modified: 2021/04/05 06:13 (external edit)