User Tools

Site Tools


java:comparable

Συγκρίνοντας αντικείμενα μεταξύ τους

Το interface java.lang.Comparable

Συχνά προκύπτει η ανάγκη να ταξινομήσουμε αντικείμενα ή να εφαρμόσουμε δυαδική αναζήτηση πάνω σε ένα ήδη ταξινομημένο σύνολο. Προκειμένου να εφαρμόσουμε τους παραπάνω αλγορίθμους είναι αναγκαίο να μπορούμε να συγκρίνουμε αντικείμενα μεταξύ τους. Ας υποθέσουμε ότι έχουμε μια σειρά από αντικείμενα της κλάσης Rectangle τα οποία θέλουμε να ταξινομήσουμε όπως παρακάτω:

Rectangle.java
class Rectangle {
 
  int width;
  int height;
 
  public Rectangle(int initWidth, int initHeight) {
    width = initWidth;
    height = initHeight;
  }
 
  int area() {
    return width * height;
  }
  public String toString() {
    return "width: "+width+", height: "+height;
  }
}
SortRectangleArray.java
import java.util.Random;
import java.util.Arrays;
 
class SortRectangleArray {
 
    public static void main(String []args) {
    Random rand = new Random(12345);
    Rectangle []rectangles = new Rectangle[5];
    for(int i=0;i<5; i++)
      rectangles[i] = new Rectangle(rand.nextInt(10), rand.nextInt(10));
 
    print_rectangles(rectangles);
    Arrays.sort(rectangles);
    print_rectangles(rectangles);
  }
 
  public static void print_rectangles(Rectangle []rectangles) {
    System.out.println("================");
    for(int i=0; i<rectangles.length; i++)
      System.out.println(i+". "+rectangles[i].toString());
  }
}

O παραπάνω κώδικας αν και μεταγλωττίζεται τερματίζει αναπάντεχα με ένα μήνυμα της μορφής:

Exception in thread "main" java.lang.ClassCastException: class Rectangle cannot be cast to class java.lang.Comparable (Rectangle is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
	at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.base/java.util.Arrays.sort(Arrays.java:1250)
	at SortRectangleArray.main(SortRectangleArray.java:13)

Ο λόγος που συμβαίνει αυτό είναι ότι δεν υπάρχει τρόπος να συγκριθούν τα αντικείμενα τύπου Rectangle μεταξύ τους. Προκειμένου να επιτευχθεί η σύγκριση, η Java αναμένει η κλάση Rectangle να υλοποιεί το interface java.lang.Comparable. Το interface java.lang.Comparable απαιτεί να υλοποιήσετε τη μέθοδο int compareTo(Rectangle r), η οποία επιστρέφει:

  • > 0 έαν το τρέχον αντικείμενο είναι μεγαλύτερο από το r,
  • < 0 εάν το τρέχον αντικείμενο είναι μικρότερο από το r.
  • == 0 εάν το τρέχον αντικείμενο είναι ίσο με το r.

Το κριτήριο μέσω του οποίου θα συγκρίνετε δύο αντικείμενα τύπου Rectangle δεν προσδιορίζεται. Ας υποθέσουμε ότι θέλουμε να κατατάξουμε τα αντικείμεμενα τύπου Rectangle με βάση το εμβαδό τους. Μπορούμε να γράψουμε τη συνάρτηση compareTo εντός της κλάση Rectangle ως εξής:

  public int compareTo(Rectangle r) {
    return this.area() - r.area();
  }

Επίσης, δεν πρέπει να παραλήψουμε να δηλώσουμε ότι η κλάση Rectangle υλοποιεί το συγκεκριμένο interface. Τελικά, η κλάση Rectangle γράφεται ως εξής:

Rectangle.java
class Rectangle implements java.lang.Comparable<Rectangle> {
 
  int width;
  int height;
 
  public Rectangle(int initWidth, int initHeight) {
    width = initWidth;
    height = initHeight;
  }
 
  int area() {
    return width * height;
  }
  public String toString() {
    return "width: "+width+", height: "+height;
  }
 
  public int compareTo(Rectangle r) {
    return this.area() - r.area();
  }
}

Οι βασικές κλάσεις του πακέτου java.lang (String,Character,Long,Integer,Short,Double,Float) υλοποιούν το interface java.lang.Comparable. Η κλάση java.lang.String υλοποιεί σύγκριση δύο Strings λεξικογραφικά.

Το interface java.util.Comparator

Στο προηγούμενο παράδειγμα, ας υποθέσουμε ότι ενώ η κλάση Rectangle υλοποιεί το interface Comparable εμείς δεν θέλουμε να κατατάξουμε τα αντικείμενα με βάση το κριτήριο σύγκρισης που εισάγει η μέθοδος compareTo (το εμβαδό του παραλληλογράμμου), αλλά με βάση το ύψος. Σε αυτή την περίπτωση μπορούμε να εκμεταλλευτούμε μια παραλλαγή της μεθόδου sort, η οποία εκτός από τον πίνακα λαμβάνει ως δεύτερο όρισμα μία κλάση που θα επιτελέσει τη σύγκριση δύο αντικειμένων μεταξύ τους. Η κλάση αυτή πρέπει υποχρεωτικά να υλοποιεί το interface java.util.Comparator. Για το προηγούμενο παράδειγμα ας δημιουργήσουμε μία τέτοια κλάση.

RectangleComparator.java
public class RectangleComparator implements java.util.Comparator<Rectangle>{
 
  public int compare(Rectangle r1, Rectangle r2) {
      return r1.height - r2.height;
  }  
}

και στη μέθοδο main να καλέσουμε την παραλλαγή της sort ως εξης:

    Arrays.sort(rectangles, new RectangleComparator());
SortRectangleArray.java
import java.util.Random;
import java.util.Arrays;
 
class SortRectangleArray {
 
    public static void main(String []args) {
    Random rand = new Random(12345);
    Rectangle []rectangles = new Rectangle[5];
    for(int i=0;i<5; i++)
      rectangles[i] = new Rectangle(rand.nextInt(10), rand.nextInt(10));
 
    print_rectangles(rectangles);
    Arrays.sort(rectangles, new RectangleComparator());
    print_rectangles(rectangles);
  }
 
  public static void print_rectangles(Rectangle []rectangles) {
    System.out.println("================");
    for(int i=0; i<rectangles.length; i++)
      System.out.println(i+". "+rectangles[i].toString());
  }
}

Παρατηρείτε ότι η σειρά της τελικής κατάταξης των αντικειμένων αλλάζει. Αγνοείται η σειρά κατάταξης που δημιουργεί η μέθοδος compareTo και εισάγεται η σειρά κατάταξης που ορίζει η παραπάνω κλάση.

Ο παραπάνω τρόπος σύγκρισης έχει ιδιαίτερη χρησιμότητα όταν έχουμε κλάσεις των οποίων θέλουμε να συγκρίνουμε τα αντικείμενα μεταξύ τους, αλλά οι κλάσεις αυτές δεν υλοποιούν το interface java.lang.Comparable.

java/comparable.txt · Last modified: 2020/03/15 18:30 (external edit)