Як порівняти об'єкти за кількома полями


237

Припустимо, у вас є деякі об'єкти, які мають кілька полів, їх можна порівняти:

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

Отже, у цьому прикладі, коли ви запитуєте, чи:

a.compareTo(b) > 0

ви можете запитати, чи прізвище має прізвище перед b, або якщо a старше b, і т. д. ...

Який найчистіший спосіб дозволити багаторазове порівняння між цими видами об’єктів, не додаючи зайвих безладів чи накладних витрат?

  • java.lang.Comparable інтерфейс дозволяє порівняти лише одне поле
  • Додавання численні порівняння методів (тобто compareByFirstName(), compareByAge()і т.д ...) захаращений на мій погляд.

То який найкращий шлях для цього?


3
чому це КВ? Це абсолютно правильне питання програмування.
Елі

2
Чи знаєте ви, що Comprable дозволяє порівняти за кількістю полів, скільки вам подобається?
DJClayworth

Відповіді:


81

Ви можете реалізувати a, Comparatorякий порівнює два Personоб'єкти, і ви можете вивчити скільки завгодно полів. Ви можете помістити змінну у свій компаратор, яка вказує, з яким полем порівняти, хоча, мабуть, було б простіше просто записати кілька компараторів.


5
Насправді я віддаю перевагу ідеї використання єдиного компаратора. Я не думаю, що ця відповідь є неправильною, але кожен, хто її читає, обов'язково повинен перевірити відповідь Стіва Куо нижче.
Феліпе Лео

Багаторазовий порівняльник був лише у тому випадку, якщо вам потрібні різні методи порівняння, які не є функцією самих даних - тобто іноді потрібно порівнювати за назвою, в інші часи за віком і т. Д. Для порівняння за кількома полями одночасно, лише один порівняльник було б необхідно.
Елі

399

З Java 8:

Comparator.comparing((Person p)->p.firstName)
          .thenComparing(p->p.lastName)
          .thenComparingInt(p->p.age);

Якщо у вас є методи аксесуара:

Comparator.comparing(Person::getFirstName)
          .thenComparing(Person::getLastName)
          .thenComparingInt(Person::getAge);

Якщо клас реалізує Порівняльний, тоді такий порівняльник може бути використаний у методі CompareTo:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}

5
Особливо лиття (Person p)важливе для ланцюгових компараторів.
члени звуку

5
Наскільки це ефективно при порівнянні великої кількості об'єктів, наприклад, при сортуванні? Чи потрібно створювати нові Comparatorекземпляри під час кожного дзвінка?
jjurm

4
Я отримую NullPointerException, коли одне з полів, з якими я порівнюю, є нульовим, як String. Чи все-таки слід зберегти цей формат порівняння, але дозволити його бути безпечним для нуля?
reveach

3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- спочатку селектор поля, потім порівняльник
gavenkoa

2
@jjurm, коли ви використовуєте його, compareToяк показано вище Comparator, створюється щоразу, коли метод викликається. Ви можете запобігти цьому, зберігаючи компаратор у приватному статичному кінцевому полі.
Гендальф

165

Ви повинні реалізувати Comparable <Person>. Якщо припустити, що всі поля не будуть нульовими (для простоти), то вік є цілим, і порівняти ранжування є першим, останнім, віковим, compareToметод досить простий:

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}

10
якщо ви реалізуєте Порівняльний <Персона>, тоді метод є порівняти до (Особа р) .., схоже, ця відповідь була змішана зі методом порівняння порівняння <T o1, T o2>
Майк

5
Це не рекомендується. Використовуйте компаратор, коли у вас є кілька полів.
Індіка

1
це найкраще рішення на даний момент, (краще, ніж більше компараторів)
Василе Сурду

4
@indika, мені цікаво: чому це не рекомендується? Порівняння використання декількох властивостей мені здається прекрасним.
ars-longa-vita-brevis

4
@ ars-longa-vita-brevis, Якщо ви використовуєте Порівнянні, то логіка сортування повинна бути в тому ж класі, об'єкти якого сортуються, так це називається природним упорядкуванням об'єктів. За допомогою Comparator ви можете записати власну логіку сортування поза класом Person. Якщо ви хочете порівнювати об'єкти Особи лише за її ім'ям чи прізвищем, ви не можете використовувати цю логіку. Ви повинні написати це ще раз,
indika

78

(від способів сортування списків об'єктів на Java на основі кількох полів )

Робочий код у цій суті

Використання лямбда Java 8 (додано 10 квітня 2019 р.)

Java 8 це чудово вирішує лямбда (хоча Guava та Apache Commons все ще можуть запропонувати більшу гнучкість):

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));

Завдяки відповіді @ gaoagong нижче .

Брудний і перекручений: сортування вручну

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        int sizeCmp = p1.size.compareTo(p2.size);  
        if (sizeCmp != 0) {  
            return sizeCmp;  
        }  
        int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);  
        if (nrOfToppingsCmp != 0) {  
            return nrOfToppingsCmp;  
        }  
        return p1.name.compareTo(p2.name);  
    }  
});  

Для цього потрібно багато набору тексту, технічного обслуговування та схильності до помилок.

Рефлексивний спосіб: сортування за допомогою BeanComparator

ComparatorChain chain = new ComparatorChain(Arrays.asList(
   new BeanComparator("size"), 
   new BeanComparator("nrOfToppings"), 
   new BeanComparator("name")));

Collections.sort(pizzas, chain);  

Очевидно, це більш стисло, але ще більше схильності до помилок, оскільки ви втрачаєте пряме посилання на поля, використовуючи натомість Strings (відсутність безпеки, автоматичне відновлення). Тепер, якщо поле перейменовано, компілятор навіть не повідомить про проблему. Більше того, оскільки це рішення використовує рефлексію, сортування відбувається набагато повільніше.

Як дістатися: сортування за допомогою Google Guava's ComparingChain

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();  
        // or in case the fields can be null:  
        /* 
        return ComparisonChain.start() 
           .compare(p1.size, p2.size, Ordering.natural().nullsLast()) 
           .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) 
           .compare(p1.name, p2.name, Ordering.natural().nullsLast()) 
           .result(); 
        */  
    }  
});  

Це набагато краще, але вимагає деякого коду пластини котла для найпоширенішого випадку використання: нульові значення повинні бути оцінені за замовчуванням менше. Для нульових полів ви повинні надати додатковій інструкції Гуаві, що робити в такому випадку. Це гнучкий механізм, якщо ви хочете зробити щось конкретне, але часто вам потрібен випадок за замовчуванням (тобто 1, a, b, z, null).

Сортування за допомогою Apache Commons CompareToBuilder

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();  
    }  
});  

Як і Guava's ComparingChain, цей клас бібліотеки легко сортує по декількох полях, але також визначає поведінку за замовчуванням для нульових значень (тобто 1, a, b, z, null). Однак ви також не можете вказати нічого іншого, якщо ви не надасте власний компаратор.

Таким чином

Зрештою, це зводиться до смаку та потреби у гнучкості (Guava's ComparingChain) проти стислого коду (Apache's CompareToBuilder).

Бонусний метод

Я знайшов хороше рішення, яке поєднує в собі декілька компараторів у порядку пріоритетності в CodeReview у MultiComparator:

class MultiComparator<T> implements Comparator<T> {
    private final List<Comparator<T>> comparators;

    public MultiComparator(List<Comparator<? super T>> comparators) {
        this.comparators = comparators;
    }

    public MultiComparator(Comparator<? super T>... comparators) {
        this(Arrays.asList(comparators));
    }

    public int compare(T o1, T o2) {
        for (Comparator<T> c : comparators) {
            int result = c.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
        Collections.sort(list, new MultiComparator<T>(comparators));
    }
}

Звичайно, Apache Commons Collection має для цього корисну програму:

ComparatorUtils.chainedComparator (comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));

22

@Patrick Щоб сортувати більше одного поля послідовно, спробуйте ComparatorChain

ComparatorChain - це компаратор, який загортає один або кілька компараторів послідовно. ComparatorChain викликає кожен Порівняльник послідовно до тих пір, поки будь-який 1) жоден одиночний компаратор не поверне нульовий результат (і цей результат потім повернеться), або 2) ComparatorChain вичерпується (і нуль повертається). Цей тип сортування дуже схожий на сортування в декількох стовпцях у SQL, і цей клас дозволяє класам Java імітувати таку поведінку під час сортування списку.

Для подальшого полегшення сортування, подібного до SQL, порядок будь-якого окремого компаратора зі списку можна> змінити.

Виклик методу, який додає нові Порівняльники або змінює сортування підйому / спадання після виклику порівняння (Об'єкт, Об'єкт), призведе до непідтримуваного ОпераціїЕксцепції. Однак подбайте про те, щоб не змінювати базовий Список компараторів або BitSet, який визначає порядок сортування.

Примірники ComparatorChain не синхронізовані. Клас не є безпечним для потоків під час створення, але він є безпечним для потоків, щоб здійснити декілька порівнянь після завершення всіх операцій налаштування.


20

Ще один варіант, який ви завжди можете розглянути, - Apache Commons. Він надає безліч варіантів.

import org.apache.commons.lang3.builder.CompareToBuilder;

Наприклад:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}


10
import com.google.common.collect.ComparisonChain;

/**
 * @author radler
 * Class Description ...
 */
public class Attribute implements Comparable<Attribute> {

    private String type;
    private String value;

    public String getType() { return type; }
    public void setType(String type) { this.type = type; }

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }

    @Override
    public String toString() {
        return "Attribute [type=" + type + ", value=" + value + "]";
    }

    @Override
    public int compareTo(Attribute that) {
        return ComparisonChain.start()
            .compare(this.type, that.type)
            .compare(this.value, that.value)
            .result();
    }

}

1
Мені дуже подобається ця стратегія. Дякую!
Містер Polywhirl

Найефективніший спосіб! Спасибі
Закарія Буацца

8

Для тих, хто може користуватися потоковим API API 8, існує чудовий підхід, який тут добре задокументований: лямбдаси та сортування

Я шукав еквівалент C # LINQ:

.ThenBy(...)

Я знайшов механізм у Java 8 на компараторі:

.thenComparing(...)

Отже, ось фрагмент, який демонструє алгоритм.

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

Ознайомтеся з посиланням, поданим вище, для отримання більш чіткого способу та пояснення того, як висновок типу Java робить дещо більш незграбним визначення у порівнянні з LINQ.

Ось повний блок тесту для довідок:

@Test
public void testChainedSorting()
{
    // Create the collection of people:
    ArrayList<Person> people = new ArrayList<>();
    people.add(new Person("Dan", 4));
    people.add(new Person("Andi", 2));
    people.add(new Person("Bob", 42));
    people.add(new Person("Debby", 3));
    people.add(new Person("Bob", 72));
    people.add(new Person("Barry", 20));
    people.add(new Person("Cathy", 40));
    people.add(new Person("Bob", 40));
    people.add(new Person("Barry", 50));

    // Define chained comparators:
    // Great article explaining this and how to make it even neater:
    // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

    // Sort the stream:
    Stream<Person> personStream = people.stream().sorted(comparator);

    // Make sure that the output is as expected:
    List<Person> sortedPeople = personStream.collect(Collectors.toList());
    Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
    Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
    Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
    Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
    Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
    Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
    Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
    Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
    Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
    // Andi     : 2
    // Barry    : 20
    // Barry    : 50
    // Bob      : 40
    // Bob      : 42
    // Bob      : 72
    // Cathy    : 40
    // Dan      : 4
    // Debby    : 3
}

/**
 * A person in our system.
 */
public static class Person
{
    /**
     * Creates a new person.
     * @param name The name of the person.
     * @param age The age of the person.
     */
    public Person(String name, int age)
    {
        this.age = age;
        this.name = name;
    }

    /**
     * The name of the person.
     */
    public String name;

    /**
     * The age of the person.
     */
    public int age;

    @Override
    public String toString()
    {
        if (name == null) return super.toString();
        else return String.format("%s : %d", this.name, this.age);
    }
}

7

Написання Comparatorвручну для такого випадку використання є жахливим рішенням ІМО. Такі спеціальні підходи мають багато недоліків:

  • Відсутнє повторне використання коду. Порушує DRY.
  • Котельня.
  • Збільшена можливість помилок.

То яке рішення?

Спочатку деяка теорія.

Позначимо пропозицію "тип Aпідтримує порівняння" через Ord A. (З точки зору програми, ви можете розглядати Ord Aяк об'єкт, що містить логіку для порівняння двох Aс. Так, так самоComparator .)

Тепер, якщо Ord Aі Ord B, то їх композиція (A, B)також повинна підтримувати порівняння. тобто Ord (A, B). Якщо Ord A, Ord Bі Ord C, тоOrd (A, B, C) .

Ми можемо поширити цей аргумент на довільну суворість і сказати:

Ord A, Ord B, Ord C, ..., Ord ZOrd (A, B, C, .., Z)

Назвемо це твердження 1.

Порівняння композитів працюватиме так само, як ви описали у своєму запитанні: перше порівняння буде випробувано спочатку, потім наступне, потім наступне тощо.

Це перша частина нашого рішення. Тепер друга частина.

Якщо ви знаєте , що Ord A, і знаєте , як перетворити Bдо A(виклик цієї функції перетворення f), то ви також можете мати Ord B. Як? Що ж, коли два Bекземпляри потрібно порівнювати, ви спочатку перетворюєте їх на Aвикористання, fа потім застосовуєте Ord A.

Тут ми відображаємо трансформацію B → Aв Ord A → Ord B. Це відоме як противаріантне відображення (або comapкоротко).

Ord A, (B → A)кома Ord B

Назвемо це твердження 2.


Тепер застосуємо це до вашого прикладу.

У вас є названий тип даних, Personякий містить три поля типу String.

  • Ми це знаємо Ord String. За заявою 1 Ord (String, String, String),.

  • Ми можемо легко записати функцію від Personдо (String, String, String). (Просто поверніть три поля.) Оскільки ми знаємо Ord (String, String, String)і Person → (String, String, String), за твердженням 2, ми можемо використовувати comapдля отримання Ord Person.

QED.


Як я реалізую всі ці концепції?

Хороша новина - це не потрібно. Вже існує бібліотека, яка реалізує всі ідеї, описані в цій публікації. (Якщо вам цікаво, як вони реалізовані, ви можете заглянути під капот .)

Ось як буде виглядати з ним код:

Ord<Person> personOrd = 
 p3Ord(stringOrd, stringOrd, stringOrd).comap(
   new F<Person, P3<String, String, String>>() {
     public P3<String, String, String> f(Person x) {
       return p(x.getFirstName(), x.getLastname(), x.getAge());
     }
   }
 );

Пояснення:

  • stringOrdє об’єктом типу Ord<String>. Це відповідає нашій оригінальній пропозиції "підтримує порівняння".
  • p3Ordце метод , який приймає Ord<A>, Ord<B>, Ord<C>і повертається Ord<P3<A, B, C>>. Це відповідає твердженню 1. ( P3означає продукт з трьома елементами. Продукт - алгебраїчний термін для композитів.)
  • comapвідповідає добре comap,.
  • F<A, B>являє собою функцію перетворення A → B.
  • p є заводським методом створення виробів.
  • Весь вираз відповідає твердженню 2.

Сподіваюся, що це допомагає.


5

Замість методів порівняння ви можете просто визначити кілька типів підкласів "Порівняння" всередині класу Person. Таким чином ви можете передати їх у стандартні методи сортування колекцій.


3

Я думаю, було б більш заплутано, якби ваш алгоритм порівняння був «розумним». Я б пішов із запропонованими вами численними методами порівняння.

Єдиним винятком для мене буде рівність. Для тестування одиниць мені було корисно переокремити.


3

Якщо користувач може замовити людину декількома способами, ви також можете десь встановити кілька констант компаратора як константи. Більшість операцій сортування та відсортованих колекцій беруть компаратор як параметр.


3
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class User {
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

}

public class MultiFieldsComparision {

    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();

        User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
        User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
        User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
        User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
        User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
        User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);
        users.add(u5);
        users.add(u6);

        System.out.println("****** Before Sorting ******");

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

        System.out.println("****** Aftre Sorting ******");

        users.sort(
                Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

    }

}

3

Реалізація цього ж коду є, якщо нам доведеться сортувати об’єкт Person на основі кількох полів.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Person {

private String firstName;
private String lastName;
private int age;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}


static class PersonSortingComparator implements Comparator<Person> {

    @Override
    public int compare(Person person1, Person person2) {

        // for first name comparison
        int firstNameCompare = person1.getFirstName().compareTo(person2.getFirstName());

        // for last name comparison
        int lastNameCompare = person1.getLastName().compareTo(person2.getLastName());

        // for last name comparison
        int ageCompare = person1.getAge() - person2.getAge();

        // Now comparing
        if (firstNameCompare == 0) {
            if (lastNameCompare == 0) {
                return ageCompare;
            }
            return lastNameCompare;
        }
        return firstNameCompare;
    }
}

public static void main(String[] args) {
    Person person1 = new Person("Ajay", "Kumar", 27);
    Person person2 = new Person("Ajay","Gupta", 23);
    Person person3 = new Person("Ajay","Kumar", 22);


    ArrayList<Person> persons = new ArrayList<>();
    persons.add(person1);
    persons.add(person2);
    persons.add(person3);


    System.out.println("Before Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }

    Collections.sort(persons, new PersonSortingComparator());

    System.out.println("After Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }
}

}

2
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist 
final Comparator<BasicDBObject> 

    sortOrder = new Comparator<BasicDBObject>() {
                    public int compare(BasicDBObject e1, BasicDBObject e2) {
                        int threshold = new Double(e1.getDouble("threshold"))
                        .compareTo(new Double(e2.getDouble("threshold")));
                        if (threshold != 0)
                            return threshold;

                        int buyRange = new Double(e1.getDouble("buyRange"))
                        .compareTo(new Double(e2.getDouble("buyRange")));
                        if (buyRange != 0)
                            return buyRange;

                        return (new Double(e1.getDouble("targetPercentage")) < new Double(
                                e2.getDouble("targetPercentage")) ? -1 : (new Double(
                                        e1.getDouble("targetPercentage")) == new Double(
                                                e2.getDouble("targetPercentage")) ? 0 : 1));
                    }
                };
                Collections.sort(objectList, sortOrder);

Я прийшов до цього питання, тому що мій код почав подобатися вашій відповіді;)
Ян Грот

0

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

Також зауважте, що, як правило, якщо a.compareTo (b) == 0, то a.equals (b) == true. Це нормально, якщо ні, але слід пам’ятати про побічні ефекти. Подивіться чудові javadocs в інтерфейсі Порівняння, і ви знайдете багато чудової інформації про це.


0

Слідкуючи за блогом, подано приклад хорошого прикутого компаратора

http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * This is a chained comparator that is used to sort a list by multiple
 * attributes by chaining a sequence of comparators of individual fields
 * together.
 *
 */
public class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    @SafeVarargs
    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
        this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee emp1, Employee emp2) {
        for (Comparator<Employee> comparator : listComparators) {
            int result = comparator.compare(emp1, emp2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

Виклик компаратора:

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );

0

Починаючи з відповіді Стіва, термінальний оператор може бути використаний:

public int compareTo(Person other) {
    int f = firstName.compareTo(other.firstName);
    int l = lastName.compareTo(other.lastName);
    return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}

0

Порівняти два об’єкти з методом хеш-коду в java легко

public class Sample{

  String a=null;
  String b=null;

  public Sample(){
      a="s";
      b="a";
  }
  public Sample(String a,String b){
      this.a=a;
      this.b=b;
  }
  public static void main(String args[]){
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","12");
      //will return true
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

      //will return false
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","13");
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

}

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

0

Зазвичай я переосмислюю такий compareTo()метод, коли це потрібно робити багаторівневим сортуванням.

public int compareTo(Song o) {
    // TODO Auto-generated method stub
    int comp1 = 10000000*(movie.compareTo(o.movie))+1000*(artist.compareTo(o.artist))+songLength;
    int comp2 = 10000000*(o.movie.compareTo(movie))+1000*(o.artist.compareTo(artist))+o.songLength;
    return comp1-comp2;
} 

Тут спочатку перевага надається імені фільму, а потім виконавцю, і нарешті, пісні Length. Ви просто повинні переконатися, що ці множники досить віддалені, щоб не перетнути межі один одного.


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