Не работает Comparator по нескольким полям

230
31 августа 2018, 11:30

В FullComparator не работает сортировка по нескольким полям (channelName, DateCreated (в обратном порядке) и Fingerprint). Сортирует только по первому. Что делаю не так?

public class FullComparator implements Comparator<Capability> {
    @Override
    public int compare(Capability o1, Capability o2) {
        if (o1 != null && o2 != null) {
            if (o1.getChannelName() != null && o2.getChannelName() != null && !o1.getChannelName().equals(o2.getChannelName()))
                return o1.getChannelName().compareTo(o2.getChannelName());
            if (o1.getFingerprint() != null && o2.getFingerprint() != null && !o1.getFingerprint().equals(o2.getFingerprint()))
                return o1.getFingerprint().compareTo(o2.getFingerprint());
            if (o1.getDateCreated() != null && o2.getDateCreated() != null && !o2.getDateCreated().equals(o1.getDateCreated()))
                return o2.getDateCreated().compareTo(o1.getDateCreated());
        }
        if (o1 == null && o2 != null)
            return 1;
        if (o1 != null)
            return -1;
        return 0;
    }
}
public class Capability implements Comparable<Capability> {
    private long id;
    private String channelName;
    private String fingerprint;
    private boolean isActive;
    private Date dateCreated;
    public Capability(long id, String channelName, String fingerprint, boolean isActive, Date dateCreated) {
    this.id = id;
    this.channelName = channelName;
    this.fingerprint = fingerprint;
    this.isActive = isActive;
    this.dateCreated = dateCreated;
}
public long getId() {
    return id;
}
public String getChannelName() {
    return channelName;
}
public String getFingerprint() {
    return fingerprint;
}
public boolean isActive() {
    return isActive;
}
public Date getDateCreated() {
    return dateCreated;
}
@Override
public String toString() {
    return "Capability{" +
            "id=" + id +
            ", channelName='" + channelName + '\'' +
            ", fingerprint='" + fingerprint + '\'' +
            ", isActive=" + isActive +
            ", dateCreated=" + dateCreated +
            '}';
}
public class DemoComparator {
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
        Capability capability1 = new Capability(1001, "A", "R", true, sdf.parse("2015-12-15"));
        Capability capability2 = new Capability(1005, "D", "Z", false, sdf.parse("2010-12-15"));
        Capability capability3 = new Capability(900, null, "O", true, sdf.parse("2013-12-15"));
        Capability capability4 = new Capability(900, "B", "L", false, sdf.parse("2008-12-15"));
        ArrayList<Capability> capabilities = new ArrayList<>();
        capabilities.add(capability1);
        capabilities.add(capability2);
        capabilities.add(null);
        capabilities.add(capability4);
        System.out.println(capabilities);
        System.out.println();
        capabilities.sort(new FullComparator());
        System.out.println(capabilities);
    }
}

И разве метод может отсортировать все поля? Тогда получается нужно объектам полями меняться, чтобы одновременно в конечном результате все поля были в нужном порядке.

Answer 1

Видимо вы не до конца понимаете смысл сортировки по нескольким полям. Проще всего рассмотреть на примере учеников.
Ученик имеет имя и фамилию, а также год рождения. Существует следующий набор учеников:

  • Иванов Сергей - 2000
  • Иванов Артем - 2000
  • Яковлев Павел - 2001
  • Сидоров Руслан - 1999

В данной ситуации сортировка по нескольким полям (Фамилия, Имя, Год рождения -> по возрастанию) предполагает, что сначала ученики будут отсортированы по полю фамилия, затем внутри групп отсортированных по фамилии произойдет сортировка по имени, затем внутри групп отсортированных по имени и фамилии произойдет сортировка по году рождения. Таким образом на выходе получим следующий список:

  • Иванов Артем - 2000
  • Иванов Сергей - 2000
  • Сидоров Руслан - 1999
  • Яковлев Павел - 2001

Если так случится, что в список добавится еще один Иванов Сергей, но 1999 года рождения, то список отсортируется следующим образом:

  • Иванов Артем - 2000
  • Иванов Сергей - 1999
  • Иванов Сергей - 2000
  • Сидоров Руслан - 1999
  • Яковлев Павел - 2001

Ваш код рабочий и вполне справляется с поставленной задачей, чтобы в этом убедиться, достаточно добавить еще один объект Capability в ваш список:

Capability capability5 = new Capability(900, "D", "L", false, sdf.parse("2008-12-15"));  
capabilities.add(capability5);

Данный объект после сортировки будет находиться после объекта с channelName A, но перед объектом с fingerPrint Z

И кстати, если используется java8, то вполне можно обойтись и без класса FullComparator:

Comparator<Capability> comp = Comparator.nullsLast(Comparator.comparing(Capability::getChannelName).thenComparing(Capability::getFingerprint).thenComparing(Capability::getDateCreated, Comparator.reverseOrder()));
capabilities.sort(comp);

Таким образом мы буквально пишем: Отсортируй список так, чтобы null элементы оказались в конце, и сортируй сначала по channelName, потом по Fingerprint, потом по DateCreate в обратном порядке.

Но будьте внимательны, если захотите добавить в лист capability3, который имеет вторым аргументом в конструкторе null (я так понимаю, это channelName), то нужно будет написать что-то вроде:

Comparator<Capability> comp = Comparator.nullsLast(Comparator.comparing(Capability::getChannelName, Comparator.nullsFirst(Comparator.naturalOrder())).thenComparing(Capability::getFingerprint).thenComparing(Capability::getDateCreated, Comparator.reverseOrder()));
capabilities.sort(comp);

То есть все то же самое, но дополнительно написать, чтобы null значения в channelName при сортировке этого поля стояли в начале.

READ ALSO
Как форматировать дату с таймзоной в просто дату?

Как форматировать дату с таймзоной в просто дату?

Как получить из этой строки 2018-07-11T09:54:40660 +0000 обьект Date?

195
ExecutorService+GreenMail

ExecutorService+GreenMail

Тестирую отправку сообщений с помощью JavaMailSender, который запускается из ExecutorService с помощью submit, все работает отлично, но в тестах GreenMail не получает...

201
Непонятный код, цикл и поля

Непонятный код, цикл и поля

Цикл: for (j = 2; j < i; j++), в чем заключается j<iКак это понять? Откуда мы придумали j?

238
Как отследить изменение в объекте?

Как отследить изменение в объекте?

Как в javascript можно отследить изменение свойства объекта и при изменении вызвать callBack? Например: objprop = true; При изменении obj

217