Как отсортировать список объектов по какому-либо свойству

145

У меня простой класс

public class ActiveAlarm {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;
}

и List<ActiveAlarm>кон. Как отсортировать в порядке возрастания по timeStarted, затем по timeEnded? Кто-нибудь может помочь? Я знаю в C ++ с универсальным алгоритмом и оператором перегрузки <, но я новичок в Java.

Дженнифер
источник
Ответ @Yishai в этом посте демонстрирует элегантное использование enum для пользовательской сортировки и групповой сортировки (несколько аргументов) с использованием компаратора.
Gunalmel

Ответы:

141

Либо сделать ActiveAlarmреализовать, Comparable<ActiveAlarm>либо реализовать Comparator<ActiveAlarm>в отдельном классе. Затем позвоните:

Collections.sort(list);

или

Collections.sort(list, comparator);

В общем, это хорошая идея , чтобы реализовать , Comparable<T>если есть один «естественный» порядок сортировки ... В противном случае (если случится хотите разобраться в определенном порядке, но может одинаково легко хотеть другой) это лучше реализовать Comparator<T>. Честно говоря, эта конкретная ситуация может пойти в любую сторону ... но я бы, вероятно, выбрал более гибкий Comparator<T>вариант.

РЕДАКТИРОВАТЬ: Пример реализации:

public class AlarmByTimesComparer implements Comparator<ActiveAlarm> {
  @Override
  public int compare(ActiveAlarm x, ActiveAlarm y) {
    // TODO: Handle null x or y values
    int startComparison = compare(x.timeStarted, y.timeStarted);
    return startComparison != 0 ? startComparison
                                : compare(x.timeEnded, y.timeEnded);
  }

  // I don't know why this isn't in Long...
  private static int compare(long a, long b) {
    return a < b ? -1
         : a > b ? 1
         : 0;
  }
}
Джон Скит
источник
1
Эта функция compare () не в Long, потому что реализация еще более тривиальна: return a - b;
Papercrane
5
@papercrane: нет, это не удалось по причинам переполнения. Посмотрим a = Long.MIN_VALUE, b = 1.
Джон Скит
3
по состоянию на API 19 (KitKat) Лонг теперь имеет.compare
Мартин Маркончини
123

С помощью Comparator

Например:

class Score {

    private String name;
    private List<Integer> scores;
    // +accessor methods
}

    Collections.sort(scores, new Comparator<Score>() {

        public int compare(Score o1, Score o2) {
            // compare two instance of `Score` and return `int` as result.
            return o2.getScores().get(0).compareTo(o1.getScores().get(0));
        }
    });

Начиная с Java 8, вы можете просто использовать лямбда-выражение для представления экземпляра Comparator.

Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });
Джигар Джоши
источник
1
Что делает compareTo()? От куда это? Где я должен это определить?
Asqiir
1
@ Asqiir getScores()является добытчиком для scoresкоторого является List<Integer>. Когда вы getScores().get(0)получаете Integerобъект. Integerуже имеет compareTo(anotherInteger)Реализованный метод, вы не должны определить.
Вален
что такое get (0)?
Али Хаки
1
Спасибо работает как шарм (я использую его без части .get (0)). Как я могу отменить заказ?
53

JAVA 8 и выше ответ (с помощью лямбда-выражений)

В Java 8 были введены лямбда-выражения, чтобы сделать это еще проще! Вместо создания объекта Comparator () со всеми его лесами, вы можете упростить его следующим образом: (используя ваш объект в качестве примера)

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted);

или даже короче

Collections.sort(list, Comparator.comparingInt(ActiveAlarm ::getterMethod));

Это одно утверждение эквивалентно следующему:

Collections.sort(list, new Comparator<ActiveAlarm>() {
    @Override
    public int compare(ActiveAlarm a1, ActiveAlarm a2) {
        return a1.timeStarted - a2.timeStarted;
    }
});

Думайте о лямбда-выражениях как о том, что вам нужно только вставить соответствующие части кода: сигнатуру метода и то, что возвращается.

Другая часть вашего вопроса была о том, как сравнивать несколько полей. Чтобы сделать это с помощью лямбда-выражений, вы можете использовать .thenComparing()функцию для эффективного объединения двух сравнений в одно:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted             
       .thenComparing ((ActiveAlarm a1, ActiveAlarm a2) -> a1.timeEnded-a2.timeEnded)
);

Приведенный выше код отсортирует список сначала по timeStarted, а затем по timeEnded(для тех записей, которые имеют одинаковые timeStarted).

И последнее замечание: легко сравнивать примитивы long или int, вы можете просто вычесть одно из другого. Если вы сравниваете объекты ('Long' или 'String'), я предлагаю вам использовать их встроенное сравнение. Пример:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.name.compareTo(a2.name) );

РЕДАКТИРОВАТЬ: Спасибо Лукас Эдер за указание мне на .thenComparing()работу.

Джон Фаулер
источник
4
Вы могли бы оценить новый API Java 8 Comparator.comparing().thenComparing()...
Лукас Эдер
1
Берегись с возвращением разницы. В случае переполнения вы можете получить неверный результат.
krzychu
2
Это ИМХО более простой метод сортировки, опять же при условии, что у вас нет очевидного естественного порядка. Вам также не нужно Collectionsбольше звонить , вы можете позвонить прямо в список. Например:myList.sort(Comparator.comparing(Address::getZipCode).thenComparing(Compartor.comparing(Address::getStreetName));
CeePlusPlus
20

Мы можем отсортировать список одним из двух способов:

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

  Collections.sort(arrayList, new Comparator<ActiveAlarm>() {
        public int compare(ActiveAlarm o1, ActiveAlarm o2) {
            //Sorts by 'TimeStarted' property
            return o1.getTimeStarted()<o2.getTimeStarted()?-1:o1.getTimeStarted()>o2.getTimeStarted()?1:doSecodaryOrderSort(o1,o2);
        }

        //If 'TimeStarted' property is equal sorts by 'TimeEnded' property
        public int doSecodaryOrderSort(ActiveAlarm o1,ActiveAlarm o2) {
            return o1.getTimeEnded()<o2.getTimeEnded()?-1:o1.getTimeEnded()>o2.getTimeEnded()?1:0;
        }
    });

У нас может быть нулевая проверка свойств, если бы мы могли использовать «Long» вместо «long».

2. Использование Comparable (естественный порядок) : если алгоритм сортировки всегда придерживается одного свойства: напишите класс, который реализует метод Comparable, и переопределите метод compareTo, как определено ниже.

class ActiveAlarm implements Comparable<ActiveAlarm>{

public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;

public ActiveAlarm(long timeStarted,long timeEnded) {
    this.timeStarted=timeStarted;
    this.timeEnded=timeEnded;
}

public long getTimeStarted() {
    return timeStarted;
}

public long getTimeEnded() {
    return timeEnded;
}

public int compareTo(ActiveAlarm o) {
    return timeStarted<o.getTimeStarted()?-1:timeStarted>o.getTimeStarted()?1:doSecodaryOrderSort(o);
}

public int doSecodaryOrderSort(ActiveAlarm o) {
    return timeEnded<o.getTimeEnded()?-1:timeEnded>o.getTimeEnded()?1:0;
}

}

вызов метода сортировки для сортировки на основе естественного порядка

Collections.sort(list);
Jagadeesh
источник
7

В java8 + это можно записать в одну строку следующим образом:

collectionObjec.sort(comparator_lamda) или comparator.comparing(CollectionType::getterOfProperty)

код:

ListOfActiveAlarmObj.sort((a,b->a.getTimeStarted().compareTo(b.getTimeStarted())))
 

или

ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))
Картикеян
источник
5
public class ActiveAlarm implements Comparable<ActiveAlarm> {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;

    public int compareTo(ActiveAlarm a) {
        if ( this.timeStarted > a.timeStarted )
            return 1;
        else if ( this.timeStarted < a.timeStarted )
            return -1;
        else {
             if ( this.timeEnded > a.timeEnded )
                 return 1;
             else
                 return -1;
        }
 }

Это должно дать вам грубую идею. Как только это будет сделано, вы можете позвонить Collections.sort()в список.

Кал
источник
4

С Java8 это можно сделать еще чище, используя комбинацию ComparatorиLambda expressions

Например:

class Student{

    private String name;
    private List<Score> scores;

    // +accessor methods
}

class Score {

    private int grade;
    // +accessor methods
}

    Collections.sort(student.getScores(), Comparator.comparing(Score::getGrade);
Матиас Г.
источник
2

Guava's ComparisonChain :

Collections.sort(list, new Comparator<ActiveAlarm>(){
            @Override
            public int compare(ActiveAlarm a1, ActiveAlarm a2) {
                 return ComparisonChain.start()
                       .compare(a1.timestarted, a2.timestarted)
                       //...
                       .compare(a1.timeEnded, a1.timeEnded).result();
            }});
远 声 远 Shengyuan Lu
источник
1

В Java вы должны использовать статический Collections.sortметод. Вот пример списка объектов CompanyRole, отсортированных сначала по началу, а затем по концу. Вы можете легко адаптироваться к своему собственному объекту.

private static void order(List<TextComponent> roles) {

    Collections.sort(roles, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            int x1 = ((CompanyRole) o1).getBegin();
            int x2 = ((CompanyRole) o2).getBegin();

            if (x1 != x2) {
                return x1 - x2;
            } else {
                int y1 = ((CompanyRole) o1).getEnd();
                int y2 = ((CompanyRole) o2).getEnd();
                return y2 - y1;
            }
        }
    });
}
Ричард Х
источник
0

Вы можете вызвать Collections.sort () и передать Comparator, который вам нужно написать, чтобы сравнить различные свойства объекта.

Liv
источник
0

Как уже упоминалось, вы можете сортировать по:

  • Заставить ваш объект реализовать Comparable
  • Или передайте ComparatorвCollections.sort

Если вы сделаете и то, и другое, оно Comparableбудет проигнорировано и Comparatorбудет использовано. Это помогает тому, что у объектов-значений есть своя логическая логика, Comparableкоторая наиболее приемлема для вашего объекта-значения, в то время как каждый отдельный вариант использования имеет свою собственную реализацию.

Алиреза Фаттахи
источник