Як сортувати Список об'єктів за якоюсь властивістю


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

Відповіді:


140

Або зробіть 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;
  }
}

1
Ця функція порівняння () не є довгостроковою, тому що реалізація є ще більш тривіальною: return a - b;
папір для кранів

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)Реалізований метод, ви не повинні визначити.
Валентин

що таке отримати (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).

Останнє зауваження: порівняти «довгі» чи «інтитні» примітиви легко. Ви можете просто відняти один від іншого. Якщо ви порівнюєте об'єкти ("Довгий" або "Рядок"), я пропоную вам використовувати їх вбудоване порівняння. Приклад:

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

EDIT: Дякую Лукашу Едеру за те, що він вказав на мене .thenComparing().


4
Можливо, ви оціните новий API Java 8 Comparator.comparing().thenComparing()...
Лукас Едер

1
Слідкуйте з різницею, що повертається. У разі переливу можна отримати неправильний результат.
krzychu

2
Це найпростіший метод сортування IMHO, знову припускаючи, що у вас немає очевидного природного порядку. Вам також більше не потрібно дзвонити 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. Використання Порівняльного (природного впорядкування) : Якщо алгоритм сортування завжди дотримується одного властивості: напишіть клас, який реалізує метод "Порівняно" та замінить метод "Порівняти", як визначено нижче

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

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



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 () і передати в компаратор, який потрібно написати, щоб порівняти різні властивості об'єкта.


0

Як згадувалося, ви можете сортувати за:

  • Здійснення вашого об'єкта реалізацією Comparable
  • Або перейти ComparatorдоCollections.sort

Якщо ви зробите обидва, Comparableзаповіт буде проігноровано та Comparatorбуде використано. Це допомагає, що об'єкти значення мають власну логіку, Comparableяка є найбільш розумною для вашого об'єкта цінностей, тоді як кожен окремий випадок використання має власну реалізацію.

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