Как сравнивать объекты по нескольким полям

237

Предположим, у вас есть несколько объектов, у которых есть несколько полей, с которыми они могут сравниваться:

public class Person {

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

    /* Constructors */

    /* Methods */

}

Так что в этом примере, когда вы спрашиваете:

a.compareTo(b) > 0

Вы могли бы спросить, если фамилия a предшествует b, или a старше b, и т. д ...

Каков самый чистый способ включить многократное сравнение между этими типами объектов, не добавляя ненужных помех и накладных расходов?

  • java.lang.Comparable интерфейс позволяет сравнивать только по одному полю
  • Добавление многочисленные сравнения методов (то есть compareByFirstName(), compareByAge()и т.д ...) загроможден на мой взгляд.

Так каков наилучший способ сделать это?

Yuval Adam
источник
3
почему это CW? Это совершенно правильный вопрос программирования.
Elie
2
Знаете ли вы, что Comparable позволяет сравнивать столько полей, сколько вам нравится?
DJClayworth

Ответы:

81

Вы можете реализовать объект, Comparatorкоторый сравнивает два Personобъекта, и вы можете исследовать столько полей, сколько хотите. Вы можете вставить переменную в ваш компаратор, которая скажет ему, с каким полем сравнивать, хотя, вероятно, было бы проще написать несколько компараторов.

Elie
источник
5
Я на самом деле предпочитаю идею использования одного компаратора. Я не думаю, что этот ответ неправильный, но любой, кто его читает, обязательно должен проверить ответ Стива Куо ниже.
Фелипе Леан
Несколько компараторов использовались только в том случае, если вы хотите использовать разные методы сравнения, которые не являются функцией самих данных, то есть иногда вы хотите сравнивать по имени, другим по возрасту и т. Д. Для сравнения по нескольким полям одновременно используется только один компаратор. было бы необходимо.
Elie
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);

Если класс реализует Comparable, то такой метод сравнения может использоваться в методе CompareTo:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}
Enigo
источник
5
Особенно актерский состав (Person p)важен для цепных компараторов.
membersound
5
Насколько это эффективно при сравнении большого количества объектов, например, при сортировке? Нужно ли создавать новые Comparatorэкземпляры при каждом вызове?
Юрм
4
Я получаю исключение NullPointerException, когда одно из сравниваемых полей имеет нулевое значение, например строку. Есть ли какой-либо способ сохранить этот формат сравнения, но позволить ему быть абсолютно безопасным?
Прибытие
3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- первый селектор полей, затем компаратор
gavenkoa
2
@jjurm, когда вы используете его, compareToкак показано выше, Comparatorсоздается каждый раз, когда вызывается метод. Вы можете предотвратить это, сохранив компаратор в закрытом статическом окончательном поле.
Гэндальф
165

Вы должны реализовать Comparable <Person>. Предполагая, что все поля не будут нулевыми (для простоты), age - это int, а сравнение ранжирования - first, last, age, 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
если вы реализуете Comparable <Person>, тогда метод сравнивать (Person p) .. кажется, что этот ответ был перепутан с сравнительным <T o1, T o2> методом сравнения
Mike
5
Это не рекомендуется. Используйте Comparator, когда у вас есть несколько полей.
индика
1
это лучшее решение на данный момент, (лучше, чем больше компараторов)
Василе Сурду
4
@indika, мне любопытно: почему это не рекомендуется? Сравнение использования более чем одного свойства мне кажется вполне подходящим.
ars-longa-vita-brevis
4
@ ars-longa-vita-brevis, если вы используете Comparable, то логика сортировки должна быть в том же классе, чьи объекты сортируются, так что это называется естественным упорядочением объектов. С помощью Comparator вы можете написать собственную логику сортировки вне класса Person. Если вы хотите сравнивать объекты Person только по имени или фамилии, вы не можете использовать эту логику. Вы должны написать это снова,
индика
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);  

Очевидно, что это более кратко, но еще более подвержено ошибкам, поскольку вы теряете прямую ссылку на поля, используя вместо этого строки (без безопасности типов, авторефакторинг). Теперь, если поле переименовано, компилятор даже не сообщит о проблеме. Более того, поскольку в этом решении используется отражение, сортировка выполняется намного медленнее.

Как добраться: сортировка с помощью Google Guava's ComparisonChain

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(); 
        */  
    }  
});  

Это намного лучше, но для некоторого случая использования требуется некоторый код пластины котла: по умолчанию нулевые значения должны оцениваться меньше. Для нулевых полей вы должны предоставить Guava дополнительную директиву, что делать в этом случае. Это гибкий механизм, если вы хотите сделать что-то конкретное, но часто вам нужен регистр по умолчанию (т. Е. 1, a, b, z, null).

Сортировка с помощью Apache Commons

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();  
    }  
});  

Как и Guar's ComparisonChain, этот библиотечный класс легко сортируется по нескольким полям, но также определяет поведение по умолчанию для нулевых значений (т. Е. 1, a, b, z, null). Тем не менее, вы не можете указать что-либо еще, если вы не предоставите свой собственный компаратор.

таким образом

В конечном счете, все сводится к вкусу и необходимости гибкости (ComparisonChain от Guava) и лаконичного кода (CompareToBuilder от Apache).

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

Я нашел хорошее решение, которое объединяет несколько компараторов в порядке приоритета 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));
    }
}

У Ofcourse Apache Commons Collections есть утилита для этого:

ComparatorUtils.chainedComparator (comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Benny Bottema
источник
22

@Patrick Для сортировки нескольких полей подряд попробуйте ComparatorChain

ComparatorChain - это компаратор, который объединяет один или несколько компараторов в последовательности. ComparatorChain вызывает каждый Comparator по очереди, пока либо 1) ни один из Comparator не вернет ненулевой результат (и этот результат затем будет возвращен), или 2) ComparatorChain исчерпан (и возвращается ноль). Этот тип сортировки очень похож на многоколоночную сортировку в SQL, и этот класс позволяет классам Java эмулировать такое поведение при сортировке списка.

Чтобы еще больше упростить сортировку по типу SQL, порядок любого отдельного компаратора в списке можно> изменить.

Вызов метода, который добавляет новые компараторы или изменяет сортировку по возрастанию / убыванию после вызова метода сравнения (Object, Object), приведет к исключению UnsupportedOperationException. Однако будьте осторожны, чтобы не изменить базовый список компараторов или BitSet, который определяет порядок сортировки.

Экземпляры ComparatorChain не синхронизируются. Этот класс не является потокобезопасным во время создания, но он обеспечивает многопотоковое выполнение нескольких сравнений после завершения всех операций настройки.

Nigel_V_Thomas
источник
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();
}
Xeroiris
источник
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
Мне очень нравится эта стратегия. Спасибо!
г-н Поливирл
Самый эффективный способ! Спасибо
Закария Буазза
8

Для тех, кто в состоянии использовать потоковый API Java 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вручную для такого варианта использования является ужасным решением IMO. Такие специальные подходы имеют много недостатков:

  • Нет повторного использования кода. Нарушает СУХОЙ.
  • Boilerplate.
  • Увеличена вероятность ошибок.

Так в чем же решение?

Сначала немного теории.

Обозначим предложение « Aсравнение опор типа » через Ord A. (С точки зрения программы вы можете думать Ord Aкак об объекте, содержащем логику для сравнения двух As. Да, точно так же 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)comap 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.

Надеюсь, это поможет.

missingfaktor
источник
5

Вместо методов сравнения вы можете просто определить несколько типов подклассов «Comparator» внутри класса Person. Таким образом, вы можете передать их в стандартные методы сортировки коллекций.

Марк Новаковский
источник
3

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

Единственным исключением для меня будет равенство. Для модульного тестирования мне было полезно переопределить .Equals (в .net), чтобы определить, равны ли несколько полей между двумя объектами (а не то, что ссылки равны).

Майкл Харен
источник
3

Если есть несколько способов, которыми пользователь может заказать человека, у вас также может быть несколько установок Comparator в качестве констант. Большинство операций сортировки и отсортированных коллекций принимают компаратор в качестве параметра.

sblundy
источник
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);
    }
}

}
оборота Sachchit Bansal
источник
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

Если вы реализуете интерфейс Comparable , вам нужно выбрать одно простое свойство для упорядочения. Это известно как естественный порядок. Думайте об этом как о умолчанию. Он всегда используется, когда нет конкретного компаратора. Обычно это имя, но ваш вариант использования может потребовать чего-то другого. Вы можете использовать любое количество других Компараторов, которые вы можете предоставить различным API коллекций, чтобы переопределить естественный порядок.

Также обратите внимание, что обычно, если a.compareTo (b) == 0, тогда a.equals (b) == true. Это нормально, если нет, но есть побочные эффекты, о которых нужно знать. Посмотрите превосходные javadocs на интерфейсе Comparable, и вы найдете много полезной информации об этом.

Марк Ренуф
источник
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;
} 

Здесь в первую очередь предпочтение отдается названию фильма, а затем исполнителю и, наконец, песне Longth. Вы просто должны убедиться, что эти множители достаточно далеко, чтобы не пересекать границы друг друга.

jayanth
источник