Как отсортировать список объектов по некоторому свойству
У меня есть простой класс
public class ActiveAlarm {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
}
и List<ActiveAlarm>
con. Как отсортировать в порядке возрастания на timeStarted
, затем на timeEnded
? Может ли кто-нибудь помочь? Я знаю в С++ с общим алгоритмом и оператором перегрузки <, но я новичок в Java.
Ответы
Ответ 1
Либо сделать ActiveAlarm
реализовать Comparable<ActiveAlarm>
, либо реализовать Comparator<ActiveAlarm>
в отдельном классе. Затем вызовите:
Collections.sort(list);
или
Collections.sort(list, comparator);
В общем, неплохо реализовать Comparable<T>
, если есть один "естественный" порядок сортировки... в противном случае (если вы возможно, хотят сортировать в определенном порядке, но могут одинаково легко хотеть другого), лучше реализовать Comparator<T>
. Эта конкретная ситуация может пойти в любом случае, если честно... но я бы, вероятно, придерживался более гибкой опции Comparator<T>
.
EDIT: Пример реализации:
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;
}
}
Ответ 2
Использование 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) {
return o2.getScores().get(0).compareTo(o1.getScores().get(0));
}
});
Ответ 3
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
).
Последнее примечание: легко сравнивать "длинные" или "int" примитивы, вы можете просто вычесть одно из другого. Если вы сравниваете объекты ( "Long" или "String" ), я предлагаю вам использовать их встроенное сравнение. Пример:
Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.name.compareTo(a2.name) );
EDIT: Спасибо Lukas Eder за то, что указали мне на функцию .thenComparing()
.
Ответ 4
Мы можем сортировать список одним из двух способов:
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);
Ответ 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()
в списке.
Ответ 6
Так как 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);
Ответ 7
Guava 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();
}});
Ответ 8
В java вам нужно использовать статический метод Collections.sort
. Ниже приведен пример списка объектов CompanyRole, отсортированных сначала, а затем и до конца. Вы можете легко адаптироваться к своему собственному объекту.
private static void order(List<TextComponent> roles) {
Collections.sort(roles, new Comparator() {
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;
}
}
});
}
Теперь список будет отсортирован:)
Ответ 9
Вы можете вызвать Collections.sort() и передать в Comparator, который вам нужно написать, чтобы сравнить различные свойства объекта.
Ответ 10
Вы можете использовать Collections.sort
и передать свой Comparator<ActiveAlarm>
Ответ 11
Как уже упоминалось, вы можете сортировать по:
- Создание объекта
Comparable
- Или передайте
Comparator
в Collections.sort
Если вы сделаете оба, Comparable
будет проигнорирован, а Comparator
будет использоваться. Это помогает тем, что объекты value имеют свой собственный логический Comparable
, который является наиболее разумным для вашего объекта значения, в то время как каждый индивидуальный вариант использования имеет свою собственную реализацию.