Чому клас Java повинен реалізовувати порівнянне?


136

Для чого Comparableвикористовується Java ? Чому хтось реалізовує Comparableв класі? Який приклад із реального життя, де потрібно реалізувати порівнянне?



Тут є хороший приклад: java67.blogspot.com/2012/10/…
james.garriss

Відповіді:


212

Ось зразок реального життя. Зверніть увагу, що Stringтакож реалізує Comparable.

class Author implements Comparable<Author>{
    String firstName;
    String lastName;

    @Override
    public int compareTo(Author other){
        // compareTo should return < 0 if this is supposed to be
        // less than other, > 0 if this is supposed to be greater than 
        // other and 0 if they are supposed to be equal
        int last = this.lastName.compareTo(other.lastName);
        return last == 0 ? this.firstName.compareTo(other.firstName) : last;
    }
}

пізніше ..

/**
 * List the authors. Sort them by name so it will look good.
 */
public List<Author> listAuthors(){
    List<Author> authors = readAuthorsFromFileOrSomething();
    Collections.sort(authors);
    return authors;
}

/**
 * List unique authors. Sort them by name so it will look good.
 */
public SortedSet<Author> listUniqueAuthors(){
    List<Author> authors = readAuthorsFromFileOrSomething();
    return new TreeSet<Author>(authors);
}

15
Я просто хочу зазначити, що часто вам хочеться переотримати equals(і, отже hashCode), щоб відповідати вашому compareToметоду. Наприклад, це необхідно, якщо ви хочете, щоб клас добре грав з a TreeSet.
підж

Чому б просто не повернутися last?
Anirban Nag 'tintinmj'

@ AnirbanNag'tintinmj ', щоб сортувати автоматично за прізвищем у випадку, якщо прізвище однакове.
OddDev

Плюс один для гарного пояснення того, чому сравнениеTo повертає Int і що це означає. Найбільш корисні.
james.garriss

1
@ user3932000: Так, це майже в усьому сенсі всіх інтерфейсів. Але зауважте, що "інші методи Java" включають методи, написані користувачем! Насправді я б стверджував, що більшість інтерфейсів використовуються кодом користувача. На більш широких кодових базах "себе" швидко стає "іншими"
Енно Шіоджі

40

Порівняльне визначає природне впорядкування. Це означає, що ви визначаєте це, коли один об'єкт слід вважати "меншим" або "більшим за".

Припустимо, у вас є купа цілих чисел, і ви хочете їх сортувати. Це досить просто, просто покладіть їх у відсортовану колекцію, правда?

TreeSet<Integer> m = new TreeSet<Integer>(); 
m.add(1);
m.add(3);
m.add(2);
for (Integer i : m)
... // values will be sorted

Але тепер припустимо, що у мене є якийсь спеціальний об'єкт, де сортування має для мене сенс, але не визначене. Скажімо, у мене є дані, що представляють райони за поштовим індексом з щільністю населення, і я хочу їх сортувати за щільністю:

public class District {
  String zipcode; 
  Double populationDensity;
}

Тепер найпростіший спосіб їх сортування - це визначити їх за допомогою природного впорядкування, застосувавши Comparable, а це означає, що існує стандартний спосіб визначення цих об'єктів для замовлення:

public class District implements Comparable<District>{
  String zipcode; 
  Double populationDensity;
  public int compareTo(District other)
  {
    return populationDensity.compareTo(other.populationDensity);
  }
}

Зауважте, що ви можете зробити рівнозначну річ, визначивши компаратор. Різниця полягає в тому, що компаратор визначає логіку впорядкування поза об'єктом . Можливо, в окремому процесі мені потрібно замовити ті самі об'єкти за поштовим індексом - в такому випадку впорядкування не обов'язково є властивістю об'єкта або відрізняється від природного впорядкування об'єктів. Ви можете використовувати зовнішній компаратор для визначення замовного замовлення на цілі числа, наприклад, сортуючи їх за алфавітним значенням.

В основному логіка впорядкування повинна десь існувати. Це може бути -

  • в самому об'єкті, якщо він природно порівнянний (розширює Порівняльні -eg цілі числа)

  • поставляється у зовнішньому компараторі, як у наведеному вище прикладі.


хороший приклад, але він повинен бути TreeSet<Integer>замість цього TreeMap<Integer>, оскільки останній не існує, TreeMaps - це завжди <Key,Value>-пари. Доречно, гіпотетична TreeMap<District, Object>спрацювала б лише в тому випадку, якщо Район реалізує Порівняльне, правда? Ще намагаюся зрозуміти це
phil294

14

Цитується з javadoc;

Цей інтерфейс накладає загальне впорядкування на об'єкти кожного класу, який його реалізує. Це впорядкування називається природним впорядкуванням класу, а метод порівняння класу - природним методом порівняння.

Списки (та масиви) об'єктів, що реалізують цей інтерфейс, можна автоматично сортувати за Collections.sort (та Arrays.sort). Об'єкти, що реалізують цей інтерфейс, можуть використовуватися як ключі в відсортованій карті або як елементи в відсортованому наборі без необхідності вказувати компаратор.

Редагувати: .. і зробив важливий трохи сміливим.


4
Я б сказав, що речення після того, кого ви виділили жирним шрифтом, так само важливе (якщо не більше).
Майкл Боргвардт

8

Те, що клас реалізує, Comparableозначає, що ви можете взяти два об’єкти з цього класу та порівняти їх. Деякі класи, як-от певні колекції (функція сортування в колекції), які зберігають об'єкти, щоб покладатися на їх порівнянність (для сортування потрібно знати, який об’єкт є "найбільшим" тощо).


8

Більшість прикладів, наведених вище, показують, як повторно використовувати існуючий порівняний об'єкт у функції CompareTo. Якщо ви хочете реалізувати своє власне CompareTo, коли ви хочете порівняти два об'єкти одного класу, скажіть об’єкт AirlineTicket, який ви хочете б сортувати за ціною (менше займає перше місце), а потім числом зупинки (знову менше, менше Ви посіли перше місце) Ви зробите наступне:

class AirlineTicket implements Comparable<Cost>
{
    public double cost;
    public int stopovers;
    public AirlineTicket(double cost, int stopovers)
    {
        this.cost = cost; this.stopovers = stopovers ;
    }

    public int compareTo(Cost o)
    {
        if(this.cost != o.cost)
          return Double.compare(this.cost, o.cost); //sorting in ascending order. 
        if(this.stopovers != o.stopovers)
          return this.stopovers - o.stopovers; //again, ascending but swap the two if you want descending
        return 0;            
    }
}

6

Простий спосіб здійснити декілька порівнянь поля - це Guava's ComparingChain - тоді ви можете сказати

   public int compareTo(Foo that) {
     return ComparisonChain.start()
         .compare(lastName, that.lastName)
         .compare(firstName, that.firstName)
         .compare(zipCode, that.zipCode)
         .result();
   }

замість

  public int compareTo(Person other) {
    int cmp = lastName.compareTo(other.lastName);
    if (cmp != 0) {
      return cmp;
    }
    cmp = firstName.compareTo(other.firstName);
    if (cmp != 0) {
      return cmp;
    }
    return Integer.compare(zipCode, other.zipCode);
  }
}

3

Наприклад, коли ви хочете мати відсортовану колекцію чи карту


Що означає "Фернандо": якщо ви зберігаєте "речі", які реалізують "Порівняно", в сортованому класі контейнерів, сортований клас контейнерів може автоматично замовляти ці "речі".
Ян Дуркан

2

Порівняльний використовується для порівняння примірників вашого класу. Ми можемо порівняти екземпляри багатьма способами, тому нам потрібно реалізувати метод compareTo, щоб знати, як (атрибути) ми хочемо порівняти екземпляри.

Dog клас:

package test;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        Dog d1 = new Dog("brutus");
        Dog d2 = new Dog("medor");
        Dog d3 = new Dog("ara");
        Dog[] dogs = new Dog[3];
        dogs[0] = d1;
        dogs[1] = d2;
        dogs[2] = d3;

        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * brutus
         * medor
         * ara
         */

        Arrays.sort(dogs, Dog.NameComparator);
        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * ara
         * medor
         * brutus
         */

    }
}

Main клас:

package test;

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        Dog d1 = new Dog("brutus");
        Dog d2 = new Dog("medor");
        Dog d3 = new Dog("ara");
        Dog[] dogs = new Dog[3];
        dogs[0] = d1;
        dogs[1] = d2;
        dogs[2] = d3;

        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * brutus
         * medor
         * ara
         */

        Arrays.sort(dogs, Dog.NameComparator);
        for (int i = 0; i < 3; i++) {
            System.out.println(dogs[i].getName());
        }
        /**
         * Output:
         * ara
         * medor
         * brutus
         */

    }
}

Ось хороший приклад, як використовувати порівнянне в Java:

http://www.onjava.com/pub/a/onjava/2003/03/12/java_comp.html?page=2


2

Коли ви реалізуєте Comparableінтерфейс, вам потрібно реалізувати метод compareTo(). Він вам потрібен для порівняння об'єктів, щоб використовувати, наприклад, метод сортування ArrayListкласу. Вам потрібен спосіб порівняння ваших об'єктів, щоб мати можливість їх сортувати. Отже, вам потрібен спеціальний compareTo()метод у вашому класі, щоб ви могли використовувати його з ArrayListметодом сортування. ThecompareTo() метод повертає -1,0,1.

Я щойно прочитав відповідний розділ у Java Head 2.0, я все ще вчуся.


1

Гаразд, але чому б просто не визначити compareTo()метод без реалізації порівнянного інтерфейсу. Наприклад клас, Cityвизначений його nameта temperatureта

public int compareTo(City theOther)
{
    if (this.temperature < theOther.temperature)
        return -1;
    else if (this.temperature > theOther.temperature)
        return 1;
    else
        return 0;
}

Це не працює. Не застосовуючи порівнянне - я отримую виняток із класної програми
Каран Ахуджа
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.