مرتب سازی پیشرفته (Advanced Sorting)
مرتب سازی پیشرفته یعنی تعریف قانونِ مرتب سازی برای اشیاء. همچنین می توانیم ترتیب متن و عدد را عوض کنیم. در جاوا این کار با «مقایسه گر (Comparator)» و «قابل مقایسه (Comparable)» انجام می شود.
چرا به مرتب سازی پیشرفته نیاز داریم؟
وقتی لیستِ شیء داریم، باید قانون بدهیم. مثلاً ماشین ها بر اساس سال مرتب شوند. بنابراین مرتب سازی معنی دار می شود.
Comparator چیست؟
«مقایسه گر (Comparator)» یک شیء با متد compare() است. این متد دو شیء می گیرد و عدد برمی گرداند: منفی یعنی اولی جلوتر، مثبت یعنی دومی جلوتر، صفر یعنی برابر.
// Sort Car objects by year
class SortByYear implements Comparator {
  public int compare(Object obj1, Object obj2) {
    Car a = (Car) obj1;
    Car b = (Car) obj2;
    if (a.year < b.year) {
      return -1;
    }
    if (a.year > b.year) {
      return 1;
    }
    return 0;
  }
}
برای استفاده، مقایسه گر را به متد مرتب سازی بده.
Comparator myComparator = new SortByYear();
Collections.sort(myCars, myComparator);
مثال کاملِ Comparator
در این مثال، لیستِ ماشین ها بر اساس سال مرتب می شود.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class Car {
  public String brand;
  public String model;
  public int year;
  public Car(String b, String m, int y) {
    brand = b;
    model = m;
    year = y;
  }
}
class SortByYear implements Comparator {
  public int compare(Object obj1, Object obj2) {
    Car a = (Car) obj1;
    Car b = (Car) obj2;
    if (a.year < b.year) {
      return -1;
    }
    if (a.year > b.year) {
      return 1;
    }
    return 0;
  }
}
public class Main {
  public static void main(String[] args) {
    ArrayList<Car> myCars = new ArrayList<Car>();
    myCars.add(new Car("BMW", "X5", 1999));
    myCars.add(new Car("Honda", "Accord", 2006));
    myCars.add(new Car("Ford", "Mustang", 1970));
    Comparator myComparator = new SortByYear();
    Collections.sort(myCars, myComparator);
    for (Car c : myCars) {
      System.out.println(c.brand + " " + c.model + " " + c.year);
    }
  }
}
استفاده از لامبدا برای Comparator
برای کوتاه تر شدن کد، از «لامبدا (Lambda)» استفاده کن.
Collections.sort(myCars, (obj1, obj2) -> {
  Car a = (Car) obj1;
  Car b = (Car) obj2;
  if (a.year < b.year) {
    return -1;
  }
  if (a.year > b.year) {
    return 1;
  }
  return 0;
});
قوانین ویژه مرتب سازی
با Comparator می توان قانون خاص ساخت. مثلاً همه زوج ها قبلِ فردها بیایند.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class SortEvenFirst implements Comparator {
  public int compare(Object obj1, Object obj2) {
    Integer a = (Integer) obj1;
    Integer b = (Integer) obj2;
    boolean aIsEven = (a % 2) == 0;
    boolean bIsEven = (b % 2) == 0;
    if (aIsEven == bIsEven) {
      if (a < b) {
        return -1;
      }
      if (a > b) {
        return 1;
      }
      return 0;
    } else {
      if (aIsEven) {
        return -1;
      } else {
        return 1;
      }
    }
  }
}
public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> myNumbers = new ArrayList<Integer>();
    myNumbers.add(33);
    myNumbers.add(15);
    myNumbers.add(20);
    myNumbers.add(34);
    myNumbers.add(8);
    myNumbers.add(12);
    Comparator myComparator = new SortEvenFirst();
    Collections.sort(myNumbers, myComparator);
    for (int i : myNumbers) {
      System.out.println(i);
    }
  }
}
Comparable چیست؟
«قابل مقایسه (Comparable)» یعنی خود شیء قانون مقایسه دارد. متد آن compareTo() است. بازهم مقدار منفی، مثبت، یا صفر برمی گرداند.
class Car implements Comparable {
  public String brand;
  public String model;
  public int year;
  public int compareTo(Object obj) {
    Car other = (Car) obj;
    if (year < other.year) {
      return -1;
    }
    if (year > other.year) {
      return 1;
    }
    return 0;
  }
}
مثال کامل با Comparable
اینجا کلاس Car خودش قابل مقایسه است. سپس Collections.sort() بدون Comparator کار می کند.
import java.util.ArrayList;
import java.util.Collections;
class Car implements Comparable {
  public String brand;
  public String model;
  public int year;
  public Car(String b, String m, int y) {
    brand = b;
    model = m;
    year = y;
  }
  public int compareTo(Object obj) {
    Car other = (Car) obj;
    if (year < other.year) {
      return -1;
    }
    if (year > other.year) {
      return 1;
    }
    return 0;
  }
}
public class Main {
  public static void main(String[] args) {
    ArrayList<Car> myCars = new ArrayList<Car>();
    myCars.add(new Car("BMW", "X5", 1999));
    myCars.add(new Car("Honda", "Accord", 2006));
    myCars.add(new Car("Ford", "Mustang", 1970));
    Collections.sort(myCars);
    for (Car c : myCars) {
      System.out.println(c.brand + " " + c.model + " " + c.year);
    }
  }
}
ترفند مرسوم در مقایسه اعداد
گاهی می توان مقایسه عددی را یک خطی نوشت. این کار کد را کوتاه می کند.
return a.year - b.year;
return b.year - a.year;
Comparator یا Comparable؟
اگر امکان تغییر کلاس را داری، Comparable ساده تر است. اما اگر کد کلاس دست تو نیست، Comparator انعطاف بیشتری می دهد.
گام های تمرینی
- کلاس Carرا بساز و لیست پر کن.
- با Comparator بر اساس مدل مرتب کن.
- سپس با Comparable بر اساس سال مرتب کن.
نکته: برای لامبدا، به لامبدا رجوع کن. همچنین برای کار با رشته ها، صفحه عبارات باقاعده را ببین.
جمع بندی سریع
- قانون را باید مشخص کنی.
- Comparator بیرون از کلاس است.
- Comparable داخل کلاس است.
- لامبدا Comparator را کوتاه می کند.
- اختلاف سال را می توان یک خطی نوشت.
